Пікірлер
@Iogoslavia
@Iogoslavia 18 күн бұрын
Great series, thank you so much for taking the time to make these videos! Its super hard to find material on type theory that is accessible for people outside academia, like me, and I am finding you videos very enlightening. One question about let bindings. Do they offer any new compared to an abstraction? To me it seems that they are more a syntactic "sugar" to put the argument of an abstraction in the beginning. like (\a . gt 3 a) 2 Seems to have the same semantics as let a = 2 in gt 3 a Is this the case? or are there things that require the let bindings to be expressed?
@adam-jones
@adam-jones 9 күн бұрын
Without types, yes the let binding is effectively syntactic sugar over an abstraction and application. However, in the simply typed lambda calculus which this series gets to later, there are slightly different typing rules to do with polymorphism that let bindings have (which can't be applied to abstraction as easily - there are some complexity proofs but I don't get into them in this series).
@piraloco5864
@piraloco5864 Ай бұрын
this was an amazing journey, thank you! <3
@viditsaxena5360
@viditsaxena5360 2 ай бұрын
repping warwick DCS (on 🔝)
@pedrogouveia3407
@pedrogouveia3407 2 ай бұрын
I have an exam tomorrow and I think you might just have saved me
@tomanderson4166
@tomanderson4166 3 ай бұрын
Really well put together series, I really appreciate all of the work that's gone into it! Thank you so much!
@adam-jones
@adam-jones 3 ай бұрын
Glad you enjoyed it! And thank you for taking the time to leave such a kind comment :)
@tammemmaaref3544
@tammemmaaref3544 4 ай бұрын
Perfect
@asitisj
@asitisj 4 ай бұрын
6:26 I d not had seen how (\1 - > ( x- > 1)) 2 reduce to 2nd expression here, unless i knew that if you apply the lambda expression to two values, you return the first value
@adam-jones
@adam-jones 4 ай бұрын
The expression is: (\x -> (y -> x)) 1 2 We apply things left to right. If I make the brackets explicit: ((\x -> (y -> x)) 1) 2 This means the x takes the value 1, so the bit inside the outer bracket above becomes (y -> 1), leaving the expression as: (\y -> 1) 2
@asitisj
@asitisj 4 ай бұрын
Love the simplicity and focus
@daviddawkins
@daviddawkins 4 ай бұрын
Many thanks for this series, I'm currently learning how to implement HM. Great first video
@adam-jones
@adam-jones 4 ай бұрын
Thanks! Hopefully you enjoy the rest of the series too :)
@daviddawkins
@daviddawkins 4 ай бұрын
@@adam-jones Yes, indeed, watched the whole lot!
@Blure
@Blure 6 ай бұрын
My interpretation of a type variable "not free in the context", is that the type variable is not bounded elsewhere by other quantifier so it can be safely bounded in the current scope. Is that correct? It is quite tricky to wrap my head around the generalisation concept. I kindly appreciate your work.
@adam-jones
@adam-jones 6 ай бұрын
Close, but not quite. It's actually that the type variable _is_ bounded everywhere else it is used. That's because where it is not bounded the type constraints should remain attached. Maybe this JavaScript code helps as an analogy: ``` // pretend this JS represents the context... let x, y; // ...and functions represent assignments in the context const t1 = () => x; const t2 = () => x; const t3 = () => y; const t4 = (y) => y; ``` Generalisation in this analogy would be like moving down the let declaration into the function. We could not generalise the x in t1 because it shares a reference with t2. i.e. if we moved the declaration then t2's x would be undefined. We could generailse the y in t3 because it is bound everywhere else it appears in the context (t4). i.e. if we moved the declaration all other definitions would be fine.
@gloverelaxis
@gloverelaxis 6 ай бұрын
thanks again for this series! the information is really wonderful but it's really hard to listen to your speech in these videos: - the overall signal volume is *very* quiet - i have to boost my headphones up to dangerous levels to hear the most quiet parts - the volume of your voice varies dramatically - you use a lilting intonation and a rapid pace of speech which most people would normally use to indicate "glossing over less-important details" or "wrapping up obvious conclusions", except you maintain that intonation over the entire video series for the overall volume: i think it would help enormously to use a "compressor" on your vocal signal, to even out the volume and boost it up to the maximum digital signal strength for the variance in speech volume: try to speak about 5-10cm further away from the mic, and project your voice much louder. this will give a much more even baseline volume, and sudden bassy content from "proximity effect" will be less jittery for intonation & pace: the overall effect is to come across as a nervousness to wrap up the speaking quickly. try and marinate in the act of speaking, re-recording the lines again and again until you don't feel any kind of rush or unease. remember you can do as many takes as you want, and you can spend as much time as you want breathing naturally during speech. try and think from the perspective of the listener: they're encountering all this information for the first time, so they're going to need even more space and time than you need; they need time to hear you deliver the information, PLUS empty time for themselves to digest it. if you try and be very deliberate about speaking slowly and comfortably, you'll naturally change your intonation from "rattling off facts" to something more varied, and which naturally matches the pace and context of the actual information you're delivering.
@adam-jones
@adam-jones 6 ай бұрын
Thanks so much for taking the time to provide detailed feedback and tips! Agree, on reflection the audio especially in the earlier videos is quite poor. I think I got a bit better towards the end of the series at all of speaking + recording + postprocessing, although still far from perfect. The tip about speaking further from mic and projecting more makes a lot of sense and I hadn't heard of that before. Will take that and all the other stuff on board when making any future recordings.
@gloverelaxis
@gloverelaxis 7 ай бұрын
Thanks a lot for this video series! I'm glad it becomes self-evident later why the name "monotype" was chosen because that seemed a very weird choice for a second there
@gloverelaxis
@gloverelaxis 7 ай бұрын
I REALLY appreciate you going to the effort to make those "stack" diagrams that clearly delineate each sub-expression, like at 7:45
@adam-jones
@adam-jones 7 ай бұрын
@@gloverelaxis Thanks for the kind feedback, glad these were helpful :)
@guppiefang
@guppiefang 8 ай бұрын
I'm trying to implement this now for my own language. One thing I'm wondering about is how these Hindley Milner monotype "type variables" map to "type variables" that you have in a generic function like you'd write in say Swift or Kotlin. You might declare such a function signature like `fun foo<T>(things: List<T>) -> Option<T>`. That example takes a list of anything and returns an optional of that same element type. When type checking the function body, it seems like `T` would be a type variable like described in these videos, but I guess the difference is that the type-checker wouldn't try to find out what T is, because the "generic" signature tells the type checker that unification doesn't need to resolve `T` to a concrete type function (Int, String, etc). Does that sound right? By the way, are you working on this stuff in academia, or ...?? Just curious why you made the videos. Thanks again.
@adam-jones
@adam-jones 8 ай бұрын
Yep, I think your proposed approach sounds good. To implement this in practice based on the code in the video, you could use the W algorithm to get a monotype and then call generalise with an empty context to get the function's generic type (this might not work - just off the top of my head). I'm a software engineer (although right now doing some other stuff short term that is closer to technical advising). This series was just an interest on the side, as I'd learnt it and figured some of the other resources weren't quite as comprehensive and practical as this. :)
@guppiefang
@guppiefang 8 ай бұрын
The symbol ⊑ for "more general than" is confusing me. Not a big deal, but I'm wondering about it. The more abstract thing is on the left, so I think of it as a bigger "set", but the symbol looks like "subset" ⊆, so I feel like it should be flipped. ?
@adam-jones
@adam-jones 8 ай бұрын
Yup, I agree it's a bit of a weird one and doesn't line up with set logic in my head. It seems to be convention, even if a little confusing. If someone does know why it's that way round would be keen to learn :)
@herlufbaggesen
@herlufbaggesen 8 ай бұрын
I don't know why it is that way, but I like to remember it by thinking that in the context of type inference the more desirable type is the least general type. So it is the best/greatest of the two and that is why ⊑ points towards it. But I have to recall that rule every time I see ⊑, so to me it is the wrong way around too!
@guppiefang
@guppiefang 8 ай бұрын
I think you meant "instantiation" not "initialization" at the end of the video there. This playlist is really helping me. Thank you!
@adam-jones
@adam-jones 8 ай бұрын
Good spot! Thanks for the correction. And glad you're finding the playlist helpful.
@guppiefang
@guppiefang 8 ай бұрын
Thanks! I'm trying to write a type checker for a scripting language and these videos are helping. Eventually I need to wrap my head around unification. So I'm working my way through your videos here. Question.... I'm confused when you say things like `List Bool` is a monotype. Isn't List a "generic" (aka polytype)? It's ∀T . List T. It seems like even at 2:35 before you introduced polytypes, they are there on the screen. I think maybe I'm a bit confused because I'm used to languages like Java, Swift, C++, etc, where the generic types like Array<T>, std::vector<T> need to be there as generics/polytypes before you can have Array<Int> etc, even if the latter are considered a monotype.
@adam-jones
@adam-jones 8 ай бұрын
So in this system `List` is a type function, which can be applied to a monotype to form a monotype. `List Bool` is a monotype: it's the type function `List` applied to the type function (that accepts no arguments) of `Bool` `∀T . List T` is a polytype: but it's not that it uses a List that makes it such. It's that it has a for-all quantifier. Technically something like `∀T . Bool` is a polytype (even if the for-all quantified variable isn't actually used - although it's a bit pointless and could then be reduced to just `Bool`). --- Maybe another way of looking at this is that `List ≈ Array`, and `∀T . List T ≈ Array<T>`? But these concepts don't always map quite right between languages.
@jinnzhu768
@jinnzhu768 9 ай бұрын
Thanks for the nice lecture, could you please create a playlist for these videos?
@adam-jones
@adam-jones 8 ай бұрын
Thanks for the kind comment! The full playlist is here: kzbin.info/aero/PLoyEIY-nZq_uipRkxG79uzAgfqDuHzot-
@ricardopieper11
@ricardopieper11 9 ай бұрын
Thank you so much for these videos.
@adam-jones
@adam-jones 9 ай бұрын
Thank you for the kind comment! Glad they are helpful.
@nrrgrdn
@nrrgrdn 10 ай бұрын
The first S3, you call the union of S1 and S2, but in typescript it would be the product?
@adam-jones
@adam-jones 10 ай бұрын
Thanks for the question! I assume you mean at 1:18? Here I was using union in the sense that if you view it as a set of mappings, S3 is effectively all the mappings in S1 and all the mappings in S2. I didn't mean it in any very technical specific sense here - sorry for any confusion caused. In TypeScript I guess this would be similar to spreading all the substitutions in (although in general this pattern doesn't work - as explored later in the video).
@nrrgrdn
@nrrgrdn 10 ай бұрын
@@adam-jones Thanks for the clarification! :-)
@jakerunzer
@jakerunzer Жыл бұрын
I've been trying to understand and implement type inference for a while. These videos have been super helpful and I can't thank you enough :D
@adam-jones
@adam-jones Жыл бұрын
Thanks for the kind comment! Glad the videos have been helpful :)
@pablomorralla3256
@pablomorralla3256 Жыл бұрын
i know it's so nerdy, but im so happy to have found a series that introduces the basics of lambda calculus and builds up to more and more complex things. thanks!
@adam-jones
@adam-jones Жыл бұрын
Glad you're happy with the series, and thanks for taking the time to leave a comment :) Does it look like the direction it builds up to is what does interest you? Or would you like to learn other things that the series doesn't cover?
@marcrofes5731
@marcrofes5731 Жыл бұрын
Wooow, I’m a self taught student, because I’m 18 years old and in Spain I am studying something similar to vocational school on computer science but nothing of programming is well taught and is as a introduction. So looking to do more things than a simple calculator program I found many concepts like type interference… And I found your series of videos and are super easy to follow. Seriously, thanks a lot!! 🙏🏻 PD: I look if you have more social network to see more content of you but I haven’t found it.
@adam-jones
@adam-jones Жыл бұрын
Thanks for the kind words Marc, I appreciate the time you've taken to leave a comment! Am glad you found the series easy to follow and are finding the topic interesting. I don't have much more similar teaching content on other social media at the moment unfortunately, but I guess maybe will add more over time :)
@alexdarlington
@alexdarlington Жыл бұрын
I always find this slightly confusing because to me a free type variable seems 'more' general than a quantified type. In the sense that a free type variable might turn out to be a quantified type, or a concrete type, but a quantified type is definitely only a quantified type and not a concrete type.
@adam-jones
@adam-jones 8 ай бұрын
I guess it's maybe like in algebra where you might be given some simultaneous equations like: 2x + 3y = 19 x + 2y = 23 And here, because it's not quantified (i.e. x and y are free variables) they are actually less general: they're fixed by other constraints. Whereas in something like: ∀x. ∀y. x^2 - y^2 = (x - y)^2 this ends up being more general, in that x and y could be many more different things and this expression still holds. Does this help at all?
@alexdarlington
@alexdarlington Жыл бұрын
Wow, such a great series, it really cleared up so many topics for me, thanks so much! ❤
@adam-jones
@adam-jones Жыл бұрын
Glad it helped, and thanks for the kind comment :)
@sunfline
@sunfline Жыл бұрын
Great series! Thanks very much!
@adam-jones
@adam-jones Жыл бұрын
Glad you enjoyed the series, and thanks for taking the time to comment :)
@fadaaszhi
@fadaaszhi Жыл бұрын
fantastic series!
@adam-jones
@adam-jones Жыл бұрын
Thanks for the kind feedback, I appreciate it!
@nrrgrdn
@nrrgrdn Жыл бұрын
you are my new favorite youtuber :*
@adam-jones
@adam-jones Жыл бұрын
Thanks! 🙌
@charstringetje
@charstringetje Жыл бұрын
Nice series 👍 The * operator in Python is defined in PEP 448 as Iterable unpacking... Because 𝚪 is a set (having duplicate proofs, doesn't make expressions any more true and, more importantly, as a sequence a list has order, and we want to use the type order, not some order of a sequence), so in pseudo-Python the set comprehension { *𝚪, x : τₐ } would be more precise.
@adam-jones
@adam-jones Жыл бұрын
Thanks for the feedback! Agreed my psuedo-Python at 1:54 is a little inconsistent as to whether the context is a list or set. I think I used a list here as in previous videos I described the context via its BNF grammar that builds it up as a list, but agreed a set or map would probably be the more sensisble data structure - and in fact in later videos where I impement Hindley-Milner in TypeScript I use a map for the context.
@charstringetje
@charstringetje Жыл бұрын
@@adam-jones Cool I haven't watched that far yet. I just felt this content deserved some tickling of the KZbin algorithm with some like and comment interaction. Why Typescript and not ReScript (or Elm or Purescript)?
@adam-jones
@adam-jones Жыл бұрын
@@charstringetje Haha thanks for playing the algorithm :) Regarding language choice, I was very tempted to do it in rescript or Purescript. However, I chose TypeScript for two main reasons: 1. I thought it would be a language that more people would know, especially as I wanted this series to be accessible to people without a significant functional programming background. 2. I'm more familiar with TypeScript (and implemented HM in TypeScript once before), and given that I already am dealing with a tricky concept didn't want to make it harder on myself by using a language I was less familiar with! (although I appreciate mapping the HM algorithms into a more functional language might be nicer with better pattern matching features).
@David-ez6je
@David-ez6je Жыл бұрын
Awesome! Thanks for your effort and for doing a video focusing on the implementation. That was really helpful!
@adam-jones
@adam-jones Жыл бұрын
Glad the implementation videos are helpful! And thank you for your feedback and kind words - the videos can take quite a while to plan, record and edit so your comment means a lot.
@mattetis
@mattetis Жыл бұрын
Something that tripped me up a bit was the var rule with "inst" (replacement). Gamma(t) will not always be a polytype, as seen in the abstraction rule, we bind x to a new unknown monotype and no replacement will happen on it. The only way we have a polytype there is from a let binding. Where e1 will first be given the monotype and then be generalized. I think 😅 Edit: And what it actually means is (I think): When we encounter a variable name, we check what general type it is bound by, but as we are actually using it in this place it must be instantiated to some specific type (so not the same as the quantified names). Therefore we strip the polytype of all quantifiers and rename them to some specific type. Example: x has poly type `forall a b . Either a b` in the context and we instantiate it to the mono type `Either t0 t1` We still don't know what t0 and t1 is here but it is left to be refined later (I think? But I don't know how)
@adam-jones
@adam-jones Жыл бұрын
I think you're correct here! To maybe help clarify a bit: 1. Our context Γ, is a list of assignments. And assignments are mappings from expressions to types, including polytypes (see kzbin.info/www/bejne/npLZl6aAgLKYkKs for a recap). So Γ(x) could be a polytype. You're right that they can only be created by let bindings (and we could imagine somehow our program is surrounded by let-in bindings defining our global helper functions, like Haskell's prelude). 2. However, algorithm W returns a tuple [Substitution, Monotype]. So to convert Γ(x) from a polytype to a monotype we need to instantiate it. We do this by replacing all the for-all quantified variables in the polytype with fresh variables. Exactly like your example in your edit, provided t0 and t1 were type variables not free anywhere else in our existing types (so they don't have any other constraints beyond how they're used in this type). 3. You're right at this point we don't know t0 and t1. However, it will be refined later by combining other constraints when we continue with the algorithm. --- For example, lets say we have `id true` (where id is the identity function). When we look at the id type from the context, we might get a type `forall a. a -> a`. We first instantiate and get something like `t0 -> t0`. At this point, we don't know what t0 is, but that's okay. So the next step is to look at the argument to id, which is 'true'. Let's say we look this up in the context, and find it has type `Bool`. Then we take a step back and look at the two with the function application rule. At some point we create a new type variable beta we'll call t1, and unify our argument to beta against the function type: unify(t0 -> t0, Bool -> t1) If we look at what has to match, we see t0 ~ Bool (because they're both on the left of the arrow) and t0 ~ t1 (because they're both on the right of the arrow). And by transitivity, Bool ~ t1. So we could get the unifying substitution { t0 |-> Bool, t1 |-> Bool }. This then gives us the overall type of `Bool -> Bool` for the function. Then we can just take the return type beta which is `Bool`. Hopefully this example helps show how that inst rule plays in to everything else, and how the constraints introduced by our fresh type variables get combined elsewhere.
@SilvianPenguin123
@SilvianPenguin123 Жыл бұрын
i cant believe i pay 27k a year for someone to explain this shit to me in a way that doesnt make sense
@gavinmacnabb4186
@gavinmacnabb4186 Жыл бұрын
Thanks 4 helping me pass my test
@adam-jones
@adam-jones Жыл бұрын
Glad it helped! And congrats on passing :)
@valentinussofa4135
@valentinussofa4135 Жыл бұрын
Great lecture series. Please add more your lecture, Sir. Thank you very much🙏🇮🇩
@adam-jones
@adam-jones Жыл бұрын
Thanks for your feedback! Happy you have been enjoying the series. More videos are being released now and over the next few weeks.
@gabe_owner
@gabe_owner Жыл бұрын
You ought to get more attention on these videos. I know it’s pretty niche but there’s got to be more than 70-ish people who would like these videos.
@adam-jones
@adam-jones Жыл бұрын
Thanks for your comment! Agreed that I think there are proabably more people who could do with finding out about these videos. I'm hoping long-term this series is a useful resource for people coming to this topic, and that people share it with others/list it as useful watching etc., so expect the views to (slowly) grow over time.
@terahlunah
@terahlunah Жыл бұрын
Thanks for this whole series, it was really helpful. Can't wait for the algorithms videos!
@adam-jones
@adam-jones Жыл бұрын
Glad you've found the series helpful! Algorithms videos are coming out now and over the next few weeks :)
@samyouguemat7152
@samyouguemat7152 Жыл бұрын
simple and easy to understand thank you !
@adam-jones
@adam-jones Жыл бұрын
Glad it was useful Samy!
@affahrizain
@affahrizain Жыл бұрын
exactly what I'm looking for! very clear explanation too! thanks for making this video)
@adam-jones
@adam-jones Жыл бұрын
Thanks for the feedback Affandy, glad it was useful 🙌
@TheOiseau
@TheOiseau Жыл бұрын
Something that's never explained with these videos, is _why_ anyone would choose to write the obscure: (\odd --> odd 3) (\x --> equals (mod x 2) 1) instead of the clear as day: if (x mod 2 == 1) return true; Is it just for nerd credentials?
@adam-jones
@adam-jones Жыл бұрын
I agree, the lambda calculus statement doesn't read perhaps as clearly on first viewing than the imperative alternative which might look like: ``` def odd: return mod(x, 2) == 1 return odd(3) ``` However, in this series the purpose of using Lambda calculus is that it's a relatively simple syntax that can be analysed (relatively) easily in terms of typing rules. By simple here, I mean in the number of building blocks you have to construct expressions - we've just got variables, application, abstraction and let-in bindings (as opposed to an imperative language, for example where you have variables, application, abstraction, assignments, return statements, if statements, for loops, while loops, switch statements etc.). This simpler set of building blocks makes it easier to analyse in terms of formal proofs, definitions and algorithms on the language itself (e.g. typing proofs, the defintion of a free variable, type inference algorithm W)
@velcomat9935
@velcomat9935 Жыл бұрын
@@adam-jones Yes ... in other words it's for nerd credentials.
@JobvanderZwan
@JobvanderZwan 8 ай бұрын
@@velcomat9935 did you miss the part where it's easier to analyse? That also includes "easier to analyse *for computers*". That's very useful for language implementers.
@TheRiseLP
@TheRiseLP 6 ай бұрын
​@@velcomat9935 He even gave you a perfectly well-explained reason for why lambda calculua is good and you just ignored it
@velcomat9935
@velcomat9935 5 ай бұрын
@@TheRiseLP ... and those reasons concern only those who live in that far off distant land called "Academia", or is it called "Utopia", or maybe it's called "Atlantis", or some such name anyway. PS: You're not from -there-, are you?
@jawad9757
@jawad9757 Жыл бұрын
I've found these videos incredibly helpful in understanding hindley milner, will you be producing any more?
@adam-jones
@adam-jones Жыл бұрын
Thank you for the kind feedback Jawad! I initially planned out a series all the way to algorithm W, but recently haven't had much spare time to make these videos so I've fallen behind a bit. I'll try to continue the series and post some videos within the next couple of weeks - that probably wouldn't have happened if not for your comment, so thank you.
@jawad9757
@jawad9757 Жыл бұрын
@@adam-jones That would be awesome, glad I could help
@emi5015
@emi5015 Жыл бұрын
at 1:10, why isn't S(b) = io?
@gustavpeter7117
@gustavpeter7117 Жыл бұрын
It should be io, but my best guess is that b should actually have been "ho" and then applying the substitution would result in "oi", but that's just my best guess what @Adam Jones actually intended to do
@adam-jones
@adam-jones Жыл бұрын
Good spot! There is an error at 1:10, you are correct S(b) = io. @Gustav is right in that I meant to write b = ho here, as case S("ho") = hi. I will try to edit or reupload the video soon to correct this mistake.
@adam-jones
@adam-jones Жыл бұрын
I've edited the video to remove this section with the error now. Thanks again Emi and Gustav!