1 00:00:04,080 --> 00:00:08,560 [ applause ] 2 00:00:10,360 --> 00:00:12,460 Yeah, I'm glad so many people still showed up 3 00:00:12,460 --> 00:00:15,000 at the very end of the conference. 4 00:00:15,000 --> 00:00:19,980 So yeah, I'm going to be talking about mostly a number of features 5 00:00:19,980 --> 00:00:22,680 that were part of the Rust language at some point, 6 00:00:22,680 --> 00:00:24,340 but no longer are. 7 00:00:24,840 --> 00:00:29,340 And, why this is the case and usually why this is a good thing. 8 00:00:30,100 --> 00:00:32,800 So, as Ryan said, I'm Marijn Haverbeke. 9 00:00:32,800 --> 00:00:36,890 If you are into Javascript, you might have seen my name before. 10 00:00:41,560 --> 00:00:44,100 Rust has been under development for a while: 11 00:00:44,100 --> 00:00:45,740 about ten years now, I think. 12 00:00:45,740 --> 00:00:50,760 First - long stretch - was just Graydon working in isolation and... 13 00:00:51,140 --> 00:00:55,020 Who knows what kind of ideas and experiments he abandoned at that point. 14 00:00:55,020 --> 00:00:59,400 There's not even a like code repository from that time public, 15 00:00:59,400 --> 00:01:01,840 so it's like the prehistory. 16 00:01:02,000 --> 00:01:03,660 And then, at some point, 17 00:01:03,660 --> 00:01:07,740 Mozilla adopted the project and assembled a team. 18 00:01:09,300 --> 00:01:12,020 From a little before that point, we do have Git history, 19 00:01:12,020 --> 00:01:14,940 and at that point, everything was discussed in issues 20 00:01:14,940 --> 00:01:17,020 and mailing lists, and there's... 21 00:01:17,620 --> 00:01:19,130 lots of records. 22 00:01:19,130 --> 00:01:23,360 I was part of this pretty much - the initial team - where we... 23 00:01:24,060 --> 00:01:28,620 moved from the original OCaml-based compiler to a Rust-based compiler, 24 00:01:28,620 --> 00:01:31,680 and we got a bunch more people involved in the language. 25 00:01:31,680 --> 00:01:33,800 This was a period of... 26 00:01:34,320 --> 00:01:36,680 like, the first real experience with the language 27 00:01:36,680 --> 00:01:39,340 and a bunch of people from different backgrounds... 28 00:01:40,740 --> 00:01:42,500 giving their opinions on the language 29 00:01:42,500 --> 00:01:45,090 and trying to push it into their favorite direction. 30 00:01:45,360 --> 00:01:48,420 And, we had a lot of like experiments and dead ends 31 00:01:48,420 --> 00:01:51,300 and overhauls and false starts and... 32 00:01:53,300 --> 00:01:54,840 just lots and lots of churn. 33 00:01:54,840 --> 00:01:58,610 We would sometimes come up with a breaking change in the morning, 34 00:01:58,610 --> 00:02:00,700 and then have a patch ready in the afternoon 35 00:02:00,700 --> 00:02:03,420 and then convince someone to merge it later on and then... 36 00:02:03,860 --> 00:02:07,550 Because there was only one codebase, we'd just fix everything right away 37 00:02:07,550 --> 00:02:09,490 and people could continue working. 38 00:02:09,490 --> 00:02:11,130 There was some... 39 00:02:11,860 --> 00:02:15,390 some trickiness with actually getting like a compiler... 40 00:02:15,390 --> 00:02:18,480 that compiles the current code after you'd make a breaking change, 41 00:02:18,480 --> 00:02:21,280 so you'd first change the compiler, then upload a snapshot, 42 00:02:21,280 --> 00:02:23,400 then change the code, and then you could - 43 00:02:23,400 --> 00:02:25,860 everyone could - proceed with the new snapshot. 44 00:02:26,020 --> 00:02:28,940 And then, of course, a year and a half ago - I think - 45 00:02:30,580 --> 00:02:33,220 the team cut version 1.0, 46 00:02:33,400 --> 00:02:37,220 and then the process changed entirely, so now it's like... 47 00:02:37,500 --> 00:02:39,320 everything stays backwards compatible. 48 00:02:39,320 --> 00:02:44,310 It's impressive how seriously they have been taking backward compatibility. 49 00:02:46,460 --> 00:02:49,930 Experiments move like RFCs move very slowly 50 00:02:49,930 --> 00:02:51,650 and there has to be a wide consensus 51 00:02:51,650 --> 00:02:54,570 and it has to fit within the current codebase. 52 00:02:55,480 --> 00:02:58,380 So, that's a whole different stage again. 53 00:02:59,140 --> 00:03:02,830 I'm going to be mostly talking about the period where I was part of the team. 54 00:03:02,830 --> 00:03:05,900 which was 2011-2012, 55 00:03:06,360 --> 00:03:08,380 and which was probably the wildest period 56 00:03:08,380 --> 00:03:13,060 in terms of features cut, features changed - stuff like that. 57 00:03:14,560 --> 00:03:16,780 So, it may seem a bit ridiculous 58 00:03:16,780 --> 00:03:19,820 that we put so much time into really complicated features 59 00:03:19,820 --> 00:03:22,300 just to end up dropping them again. 60 00:03:23,360 --> 00:03:27,500 But, I think it's kind of an essential part of getting a complex design 61 00:03:27,500 --> 00:03:30,020 like a programming language right that, 62 00:03:30,490 --> 00:03:33,420 unless you're a super-genius, you won't really see in advance 63 00:03:33,420 --> 00:03:35,760 what the implications and the interactions between 64 00:03:35,760 --> 00:03:40,460 the various parts of the system are and you have to try it and see 65 00:03:40,620 --> 00:03:43,760 how well you can make it work and how well it fits into the system, 66 00:03:43,760 --> 00:03:46,370 and sometimes you later have to just... 67 00:03:46,720 --> 00:03:48,300 abandon it again. 68 00:03:48,980 --> 00:03:53,240 I think that's part of a healthy design process for like... 69 00:03:53,620 --> 00:03:56,840 mere mortals who need to actually see how something works 70 00:03:56,840 --> 00:03:59,380 before they can evaluate it. 71 00:03:59,900 --> 00:04:03,800 I'm structuring this talk about... around a number of visions 72 00:04:03,800 --> 00:04:07,020 that were part of the language and then dropped again. 73 00:04:07,480 --> 00:04:12,040 And, I'll try to explain why I think that, in every case, 74 00:04:12,040 --> 00:04:14,520 it was a real good decision to drop them. 75 00:04:15,420 --> 00:04:17,899 But, it's still interesting to see 76 00:04:18,640 --> 00:04:22,200 what the original visions were and what we did end up with. 77 00:04:22,820 --> 00:04:27,740 So, these are typestate, a structural type system and 78 00:04:28,740 --> 00:04:32,860 lightweight processes, and finally, garbage collection. 79 00:04:33,960 --> 00:04:36,120 Let's start with typestate. 80 00:04:38,140 --> 00:04:40,830 Typestate is... it was actually 81 00:04:40,830 --> 00:04:44,560 an important point in initial announcements of the language. 82 00:04:44,560 --> 00:04:46,720 and people were very excited about it. 83 00:04:47,920 --> 00:04:50,780 What typestate does is basically allows you to... 84 00:04:51,220 --> 00:04:55,600 allows the compiler to know more about value than just its type. 85 00:04:55,600 --> 00:04:57,100 So, an example would be... 86 00:04:57,560 --> 00:04:59,320 This is something of type [sockets], 87 00:04:59,320 --> 00:05:04,080 but we also happen to know that it's open or this is something of type array, 88 00:05:04,080 --> 00:05:07,280 but we happen to know... or say vector in the current terminology, 89 00:05:07,280 --> 00:05:09,640 but we happen to know that it's not empty. 90 00:05:09,820 --> 00:05:13,480 Something like that allowing you to add 91 00:05:13,640 --> 00:05:17,380 more safety to your program - more static guarantees. 92 00:05:19,380 --> 00:05:23,320 So, when you're programming, you usually have some mental model 93 00:05:23,320 --> 00:05:26,900 of why the thing you are doing right now is... 94 00:05:27,240 --> 00:05:29,980 is valid - is not going to crash - 95 00:05:30,030 --> 00:05:32,130 like if you're not just making random changes 96 00:05:32,130 --> 00:05:35,480 and seeing if the [tests pass], you will have 97 00:05:35,660 --> 00:05:39,440 some mental model of your... program. 98 00:05:39,600 --> 00:05:42,040 And, to a certain degree, depending on the language, 99 00:05:42,040 --> 00:05:44,840 you can tell the compiler about this model 100 00:05:44,840 --> 00:05:46,400 and the compiler can then check 101 00:05:46,400 --> 00:05:49,040 whether you are applying your model consistently. 102 00:05:49,040 --> 00:05:53,160 So, the simple case is just types - that you're actually passing the type 103 00:05:53,160 --> 00:05:55,260 that you think you are passing somewhere. 104 00:05:55,260 --> 00:05:58,700 And, if you don't, then, instead of finding out at runtime, 105 00:05:58,700 --> 00:06:01,500 you find out at compile time - and this is nice. 106 00:06:01,500 --> 00:06:03,040 There's a kind of... 107 00:06:04,420 --> 00:06:07,960 this computer does not have the fonts that my computer had but... 108 00:06:07,960 --> 00:06:10,300 Imagine arrowheads on both sides. 109 00:06:10,300 --> 00:06:13,540 There's a kind of spectrum on which languages fall 110 00:06:14,140 --> 00:06:16,040 in terms of how much 111 00:06:16,820 --> 00:06:19,140 you can actually communicate to the compiler, 112 00:06:19,140 --> 00:06:21,070 so down on one side, there's Javascript, 113 00:06:21,070 --> 00:06:23,920 and it's like... syntactically correct. 114 00:06:23,920 --> 00:06:25,700 Okay, let's go ahead - let's run it. 115 00:06:25,700 --> 00:06:28,720 And then, there's like, on the... way on other side, 116 00:06:28,720 --> 00:06:32,400 there's languages like Coq, which require you to actually construct 117 00:06:32,400 --> 00:06:37,200 a formal proof that your program does what it's supposed to do - 118 00:06:37,200 --> 00:06:40,400 that it does so in bounded time, in bounded space, 119 00:06:42,420 --> 00:06:45,940 which means you're making a lot less mistakes. 120 00:06:45,940 --> 00:06:48,250 But, on the other hand, it's like a major... 121 00:06:49,060 --> 00:06:53,280 a major project to write a small program in such a language. 122 00:06:54,240 --> 00:06:57,840 And, there is a reason that not everyone is writing their web servers 123 00:06:57,840 --> 00:06:59,360 in Coq or whatever. 124 00:06:59,360 --> 00:07:01,180 And, Rust kind of falls in the middle. 125 00:07:01,180 --> 00:07:03,980 It does have quite a bit of static guarantees 126 00:07:03,980 --> 00:07:05,840 and it helps quite a lot. 127 00:07:06,240 --> 00:07:11,240 But, it still aims to be ergonomic, like easy to program in, 128 00:07:12,140 --> 00:07:14,400 where you don't have to 129 00:07:14,660 --> 00:07:17,740 spend too much time working on these things. 130 00:07:20,500 --> 00:07:25,240 And I... one way to see the history of programming languages is kind of... 131 00:07:26,400 --> 00:07:28,780 one aspect of it at least is 132 00:07:28,780 --> 00:07:32,960 that we've been finding better and better vocabulary to... 133 00:07:33,140 --> 00:07:37,560 to describe these things we know about our program to the compiler 134 00:07:39,360 --> 00:07:41,100 in a way that's actually convenient. 135 00:07:41,100 --> 00:07:43,360 So, if you have a really terrible type system, 136 00:07:43,360 --> 00:07:46,060 that's often worse than no type system at all. 137 00:07:46,060 --> 00:07:49,120 If I have to choose to write something in Java or Javascript, 138 00:07:49,120 --> 00:07:51,780 [I'll just take Javascript, thank you very much.] 139 00:07:52,130 --> 00:07:53,760 But, we're getting better at this, 140 00:07:53,760 --> 00:07:55,975 and Rust is making a big contribution here, 141 00:07:55,975 --> 00:07:58,600 and like bringing a real, modern type system 142 00:07:58,600 --> 00:08:00,420 to the systems programming space. 143 00:08:00,420 --> 00:08:03,120 And the ownership model is - I think - 144 00:08:03,120 --> 00:08:04,550 just really, really good. 145 00:08:04,550 --> 00:08:07,770 I unfortunately wasn't on the team anymore when this was introduced, 146 00:08:07,770 --> 00:08:09,680 so I can't take any credit for it, but.. 147 00:08:09,680 --> 00:08:12,180 I think that this is the most exciting part of Rust 148 00:08:12,180 --> 00:08:14,420 and it's exactly this kind of thing where you... 149 00:08:14,420 --> 00:08:17,960 where the compiler knows what you're trying to do and 150 00:08:17,960 --> 00:08:21,320 tells you when you're violating your model. 151 00:08:22,800 --> 00:08:24,760 So, back to typestate. 152 00:08:26,200 --> 00:08:28,360 It looked somewhat like this. 153 00:08:29,640 --> 00:08:31,880 You could define predicates, 154 00:08:31,880 --> 00:08:35,140 which is this "pure function not empty" at the top 155 00:08:38,679 --> 00:08:42,059 The extra information that the compiler had about your values 156 00:08:42,059 --> 00:08:44,820 came in the form of this predicate hold 157 00:08:44,820 --> 00:08:48,570 But, these were actually just predicates written in normal Rust code 158 00:08:48,570 --> 00:08:50,050 that were supposed to be pure. 159 00:08:50,050 --> 00:08:53,780 There was a concept of an fx system at that point - which is also gone now. 160 00:08:53,780 --> 00:08:59,320 But, they just took a value and said: "Okay, I hold or I don't hold." 161 00:09:00,500 --> 00:09:04,920 And, then you could define for your functions, 162 00:09:04,920 --> 00:09:07,020 preconditions and postconditions. 163 00:09:07,020 --> 00:09:08,980 So, you could say, for example, 164 00:09:08,980 --> 00:09:13,440 this function "last" here demands that its first argument 165 00:09:13,440 --> 00:09:16,520 has the "not empty" predicate holding on it, 166 00:09:17,100 --> 00:09:20,620 because you can't take the last element from an empty array... 167 00:09:24,440 --> 00:09:27,240 Then, before you could pass such a value to such a function 168 00:09:27,240 --> 00:09:31,560 you'd have to convince the compiler that this predicate held at this point. 169 00:09:31,760 --> 00:09:34,140 For some things, this worked relatively well: 170 00:09:34,140 --> 00:09:36,900 the compiler was very clever in propagating its information 171 00:09:36,900 --> 00:09:39,480 through the control flow graph and like taking it 172 00:09:39,480 --> 00:09:42,360 from the post conditions of the functions you called. 173 00:09:43,520 --> 00:09:45,380 But here, you have, for example, 174 00:09:45,380 --> 00:09:48,920 I create an array and then I want to pass it to last. 175 00:09:49,120 --> 00:09:52,180 But, it's not okay: I first have to... 176 00:09:53,040 --> 00:09:54,820 check that it's not empty. 177 00:09:54,820 --> 00:09:58,940 And, this is actually...a sink check would insert a runtime test, 178 00:09:58,940 --> 00:10:03,020 call to the predicate, and then panic if it failed. 179 00:10:05,900 --> 00:10:08,160 But actually, I mean, this array isn't empty: 180 00:10:08,160 --> 00:10:09,640 this is very easy to prove. 181 00:10:09,640 --> 00:10:12,700 But, because the compiler only saw these predicates as 182 00:10:12,700 --> 00:10:15,000 like opaque pieces of code, 183 00:10:15,180 --> 00:10:17,240 it couldn't actually reason about them - 184 00:10:17,240 --> 00:10:19,460 it could only take what you told it. 185 00:10:19,460 --> 00:10:22,000 Like, if you checked there was variance of check, 186 00:10:22,280 --> 00:10:26,140 one of them which just was like an unsafe form of 187 00:10:26,140 --> 00:10:27,570 "just believe me: this holds." 188 00:10:27,570 --> 00:10:29,740 So, that might also have been appropriate here 189 00:10:29,740 --> 00:10:32,440 because I'm really sure that this array is not empty. 190 00:10:34,200 --> 00:10:37,320 And then, there was a one version that 191 00:10:38,360 --> 00:10:41,540 ensured that the compiler already statically knew at this point 192 00:10:41,540 --> 00:10:44,840 that something else... that it was kind of an assertion of "okay... 193 00:10:46,140 --> 00:10:50,160 "I must notice at this point, but don't insert a runtime check. 194 00:10:50,720 --> 00:10:54,380 "I want to have a static error if it's not provable." 195 00:10:56,300 --> 00:10:59,660 But, in my experience, the effect of this system 196 00:10:59,660 --> 00:11:03,640 was mostly that you would be littering your code with check statements 197 00:11:03,640 --> 00:11:06,380 and they would also panic at runtime. 198 00:11:06,380 --> 00:11:09,740 So, the amount of static guarantees wasn't very great 199 00:11:09,740 --> 00:11:13,520 because often... usually the compiler couldn't really help a lot with 200 00:11:13,520 --> 00:11:17,260 like reasoning about when they actually held and when they didn't. 201 00:11:18,180 --> 00:11:20,970 It was in the compiler for a long time still 202 00:11:20,970 --> 00:11:24,400 but eventually it was dropped because it was just not pulling its weight. 203 00:11:24,400 --> 00:11:27,660 So, in terms of experiments in good expressive ways 204 00:11:27,660 --> 00:11:30,960 to express these kind of things, I think this was a failed experiment. 205 00:11:30,960 --> 00:11:34,000 It existed in some research languages before 206 00:11:34,000 --> 00:11:38,000 but it's never really made it into a big mainstream type language - 207 00:11:38,380 --> 00:11:40,100 for good reason, I think. 208 00:11:41,580 --> 00:11:43,440 So, so much for that. 209 00:11:43,820 --> 00:11:46,780 Next topic is "structural typing." 210 00:11:47,060 --> 00:11:51,720 So, in typing systems, you have two concepts 211 00:11:52,520 --> 00:11:54,520 where structural typing is... 212 00:11:55,380 --> 00:11:58,380 say you have a function type, which has a few arguments types, 213 00:11:58,380 --> 00:12:00,480 and a return type, and you want to compare it 214 00:12:00,480 --> 00:12:02,040 to another function type. 215 00:12:02,040 --> 00:12:05,740 So, you're just going to look at the fields in the function: 216 00:12:05,740 --> 00:12:07,960 does it have the same amount of arguments, 217 00:12:08,240 --> 00:12:12,660 are its arguments of compatible types, is it return type of compatible type? 218 00:12:13,240 --> 00:12:15,000 And, that's structural. 219 00:12:15,140 --> 00:12:17,260 On the other hand, there is a nominal typing, 220 00:12:17,260 --> 00:12:19,630 where you just say: "Where is this type declared? 221 00:12:19,630 --> 00:12:22,440 "What's the name of this type?" - and it has to be the same. 222 00:12:22,440 --> 00:12:25,060 So, Rust's structs currently work this way, 223 00:12:25,400 --> 00:12:28,440 as do enums - two types are only compatible 224 00:12:28,440 --> 00:12:31,200 if they are actual instances of the thing that was declared 225 00:12:31,200 --> 00:12:33,800 in the same point in the code. 226 00:12:36,360 --> 00:12:38,760 Initially, structs were structural types. 227 00:12:38,760 --> 00:12:43,420 So, this curly braces thing there is syntax for a struct type, 228 00:12:43,420 --> 00:12:47,540 with two fields - x and y - of type "float." 229 00:12:48,220 --> 00:12:52,120 And, the type declaration just defines an alias for the type. 230 00:12:53,180 --> 00:12:58,120 This is just like a name for the type record with two float fields. 231 00:12:59,960 --> 00:13:03,560 So, if I define a function, which takes an argument of this point, 232 00:13:03,740 --> 00:13:08,460 I can call it with just a record constructed on the fly 233 00:13:08,460 --> 00:13:10,940 without any record name involved. 234 00:13:11,920 --> 00:13:13,820 Records themselves don't have a name: 235 00:13:13,820 --> 00:13:16,440 they just have a structure in this system, and... 236 00:13:17,320 --> 00:13:19,940 it's kind of nice and lightweight and minimal, 237 00:13:19,940 --> 00:13:23,560 and often you don't even bother to give your record a name 238 00:13:23,560 --> 00:13:25,480 if you only use it a few times. 239 00:13:26,560 --> 00:13:29,440 So, where you would now probably use it triple, 240 00:13:29,440 --> 00:13:33,500 you could use a record with nice descriptive field names. 241 00:13:33,500 --> 00:13:36,540 I kind of liked it for programming with... 242 00:13:38,940 --> 00:13:42,100 But, I'll come back later to why this part was removed. 243 00:13:43,300 --> 00:13:46,200 Another aspect of this was object types, 244 00:13:46,540 --> 00:13:49,080 whereas structure types were only compatible 245 00:13:49,080 --> 00:13:52,660 if they had actually the exact same fields and... 246 00:13:53,800 --> 00:13:54,880 in the same order. 247 00:13:54,880 --> 00:13:58,140 They weren't reordered because of C compatibility 248 00:13:58,140 --> 00:14:02,000 and they had to be the exact same to be able to compile it efficiently 249 00:14:02,000 --> 00:14:05,260 because then all code that interacted with such a record 250 00:14:05,260 --> 00:14:07,440 knew how it was laid out in memory. 251 00:14:07,640 --> 00:14:12,440 Objects were a more dynamic feature, and here... 252 00:14:14,320 --> 00:14:19,020 any object type that has a subset of the fields - 253 00:14:19,020 --> 00:14:21,780 fields are always methods - so they're always functions. 254 00:14:23,300 --> 00:14:26,020 This object type has, its compatible, so I could, 255 00:14:26,660 --> 00:14:29,800 if I define the type - a collection of T - with these two - 256 00:14:29,800 --> 00:14:33,200 it's probably not a very great abstraction but just bear with me - 257 00:14:33,200 --> 00:14:36,540 with a length and an item accessor method, 258 00:14:37,140 --> 00:14:41,440 you could take any object that has I don't know what kind of... 259 00:14:42,360 --> 00:14:44,280 methods with also these two, 260 00:14:44,280 --> 00:14:46,840 and you could treat it as a collection of T. 261 00:14:48,160 --> 00:14:52,240 So, these were both the types of the concrete objects 262 00:14:52,240 --> 00:14:55,980 and also serve the role of interfaces, which is kind of nice in terms of 263 00:14:55,980 --> 00:14:59,700 how many concepts you need to do object-oriented programming. 264 00:14:59,990 --> 00:15:02,180 And, you could also even use it as a kind of... 265 00:15:03,780 --> 00:15:07,700 checked duck typing, where you define your function 266 00:15:07,700 --> 00:15:09,970 and you just say: "I'm only going to call length 267 00:15:09,970 --> 00:15:11,820 "on the thing that I'm getting," 268 00:15:12,140 --> 00:15:15,180 and then, anything that had a length method could be passed in - 269 00:15:15,180 --> 00:15:18,460 you don't even need to formally define an interface name or anything, 270 00:15:18,460 --> 00:15:21,660 it's just all like structural by name. 271 00:15:24,460 --> 00:15:26,300 So... 272 00:15:27,840 --> 00:15:29,300 One implication of this was 273 00:15:29,300 --> 00:15:31,920 that because code that used them didn't know their size, 274 00:15:31,920 --> 00:15:33,680 they always had to be heap allocated. 275 00:15:33,680 --> 00:15:36,360 I think they even always had to be garbage collected. 276 00:15:37,600 --> 00:15:42,560 Any calls to them will be going through a dispatch table - a vtable. 277 00:15:43,200 --> 00:15:46,400 So, they're so much more heavyweight... 278 00:15:47,620 --> 00:15:49,520 compared to the rest of the language. 279 00:15:50,000 --> 00:15:52,440 We were finding that, in the compiler, 280 00:15:52,440 --> 00:15:54,460 we were kind of shying away from them 281 00:15:54,460 --> 00:15:57,860 and that we absolutely needed polymorphism because 282 00:15:58,820 --> 00:16:02,180 they were more heavyweight than necessary in many situations. 283 00:16:05,740 --> 00:16:07,540 Then, at some point... 284 00:16:09,240 --> 00:16:10,650 Well, there was also 285 00:16:10,650 --> 00:16:13,040 a lot of machinery involved in actually doing this, 286 00:16:13,040 --> 00:16:17,480 like upcasting to a type with less methods 287 00:16:17,480 --> 00:16:20,120 because then you needed to allocate a new vtable, 288 00:16:20,120 --> 00:16:23,120 preferably statically, which forwarded to the alt object 289 00:16:23,120 --> 00:16:24,640 and created a wrapper. 290 00:16:26,080 --> 00:16:29,080 It was conceptually simple but not terribly simple to implement. 291 00:16:29,080 --> 00:16:31,460 And then, at some point... 292 00:16:32,480 --> 00:16:35,000 we got more [Haskell?] people on the team 293 00:16:35,000 --> 00:16:37,000 and we all started agitating for 294 00:16:37,480 --> 00:16:40,250 a typed class kind of implementation 295 00:16:40,250 --> 00:16:42,860 interface thing that we ended up with now. 296 00:16:44,400 --> 00:16:47,220 And, because no one really liked these objects very much, 297 00:16:47,220 --> 00:16:49,800 we migrated to that. 298 00:16:50,220 --> 00:16:54,520 And, I think they just fit with the language much better. 299 00:16:54,520 --> 00:16:57,500 They don't require you to put something on the heap. 300 00:16:57,500 --> 00:17:03,520 They don't require indirect calls unless you actually are using polymorphism. 301 00:17:04,660 --> 00:17:06,660 So, I think that's a win. 302 00:17:08,200 --> 00:17:15,180 But, now that we had implementations, which effect a specific type, 303 00:17:16,680 --> 00:17:18,820 structural records also became problematic, 304 00:17:18,820 --> 00:17:22,010 because if you're using a record that happens to have the same shape 305 00:17:22,010 --> 00:17:24,240 in two completely independent contexts 306 00:17:24,620 --> 00:17:29,130 and they both define say a two string implementation of it, 307 00:17:29,130 --> 00:17:30,190 then these will clash, 308 00:17:30,190 --> 00:17:33,340 even though the actual usages have nothing to do with each other, 309 00:17:33,340 --> 00:17:36,079 they will both be trying to implement the same interface - 310 00:17:36,079 --> 00:17:37,260 the same trait on it. 311 00:17:38,200 --> 00:17:40,160 That doesn't work, so... 312 00:17:40,340 --> 00:17:43,340 Well, no one cared that much about structural records either, 313 00:17:43,340 --> 00:17:47,100 so they became nominal for this reason at that point. 314 00:17:47,420 --> 00:17:48,330 And now, of course, 315 00:17:48,330 --> 00:17:50,640 people say functions are still structural - 316 00:17:50,640 --> 00:17:53,280 they don't make much sense in any other way. 317 00:17:53,280 --> 00:17:58,000 But, the like heavy emphasis on structural typing was abandoned... 318 00:17:59,220 --> 00:18:01,220 again for good reasons.