Note: the problem in the video has various solutions which are more or less elegant. The difference between them is subtle, so I totally understand if you disagree with the proposed solution with match types.
Great video as always! Just one thing, I would make a note about the possibility of extending this in a future and relate it with this not being possible in Scala 2. - As already mentioned (even by you) this can be implemented in Scala 2 using a typeclass (with a path-dependant type) + the aux pattern (for better type inference). Now, I will not debate the fact that for a small set of types then the match type version is cleaner (and in general I really like them for things like mapping ADTs) but if this is an operation that we would expect to be applied to multiple types and maybe the ones that will provide implementations for those new types will be downstream users, then the match type is just too restrictive and a typeclass becomes a more flexible / general / powerful technique. So I believe it is worth to mention that. Which, again, is not to say that match types are always "worse" than typeclasses; I actually find them better for small / contained cases.
It may be even better to implement this with typeclasses since with match types all cases are stuffed into one function making addition of new cases problematic when that function comes from a library.
@@mykytal8991 It depends, if you are writing a library type-classes are probably a preferable solution, but when you are writing application, match type approach is safer (as far as I am aware if you mess-up pattern match branches you get compile time error) and a lot simpler.
Awesome set of videos. Keep up the good work. Your style of explanation is easy, concise and better than reading a blog. Must confess I hate that’s scala3 mix and matches indentation and brackets. I have to work in python 30% of the time and it’s the thing I dislike most stylistically of the language..
This was some great coverage. Awesome video. Yours is one of the few channels that make me drop everything to watch whenever you post something. As a side question: Do you think the lastComponentOf method would work better as a transparent inline method (return type being transparent at compile time based on an inlined match statemend therefore no match type needed) or do you think the match type would be a better way since it also provides some form of direct documentation?
Wow, such high praise, thank you! Yes, I think match types are the best solution for this kind of problem. Sure, you can use inlining or other code expansion tools to get to the same result, but nothing comes close to the cleanliness of this one as far as I'm aware.
Nice video, but still need to explore more the usage. It seems it would help me in a Scala 2 project where I wanted to tie the type of a Question with the Type of the answer. There was no way to express this in types. Could be nice also for functions that translate between different ADT.
The generics doesnt necessarily mean it has to be directly from the type argument. Both OOP and FP can use generics. The sample is simply type lambda that takes a type argument as a generic type.
I was able to write accumulate in this manner: def accumulate[T](accumulator: T, value: ConstituentPartOf[T]): T = (accumulator match case b: BigInt => b + value.asInstanceOf[ConstituentPartOf[BigInt]] case s: String => s + value.asInstanceOf[ConstituentPartOf[String]] case l: List[_] => value.asInstanceOf[ConstituentPartOf[l.type]] :: l).asInstanceOf[T] Not incredibly concise for the function author with all of the compiler hints, but I believe still type-safe as far as the user is concerned. EDIT: Turns out there is no real compiler safety here inside the function itself, as I was able to add this nonsensical case: `case d: Double => d + value.asInstanceOf[ConstituentPartOf[String]]`. But as the "library" user, you would not be able to call the function in a way to actually exercise that case.
about 10:35 - you said this is not possible in scala 2, but what about just implementing 3 methods instead of one case block? it's 100% compile time safe.
@@rockthejvm I have the same concern. When you define the match type there's no way to extend it to new types (ConstituentPartOf is "closed") after it was defined. The "lastComponentOf" function is "closed" since I can't extend it without modifying the function itself (adding another case) I'm not too familiar with Scala (mostly Haskell) but couldn't you get the same behavior using type classes? Defining something like "ConstituentPartOf[A,B] where lastComponentOf[A, B](src: A): B" (not sure about the syntax btw)
@@rockthejvm What if i want to extend a match type to match more distinct pair of types, but in a different file? How would you do that? Everything seems to require to be inside one big match statement
Great video, thanks! For me as a client of your awesome new lastComponentOf API, can I extend the API to other types? For example, suppose I want to be able to get lastComponentOf a SortedMap. Is there a way for me to add that functionality without modifying your library?
This use-case assumes you're offering the library under a single API, so if you want to support an additional type, you can add cases in the match type + implementation and your single API will still work.
@@rockthejvm I think this presumes that I have access to the library source code. I'm asking if there is any way to extend the type match if I DON'T have access to modify the library code. (I'm guessing the answer is no, that's what type classes are for?)
Note: the problem in the video has various solutions which are more or less elegant. The difference between them is subtle, so I totally understand if you disagree with the proposed solution with match types.
I like this solution. My only concern is that it doesn't seem extensible. Perhaps extensible match types will be part of scala 3.1?
Great video as always!
Just one thing, I would make a note about the possibility of extending this in a future and relate it with this not being possible in Scala 2. - As already mentioned (even by you) this can be implemented in Scala 2 using a typeclass (with a path-dependant type) + the aux pattern (for better type inference).
Now, I will not debate the fact that for a small set of types then the match type version is cleaner (and in general I really like them for things like mapping ADTs) but if this is an operation that we would expect to be applied to multiple types and maybe the ones that will provide implementations for those new types will be downstream users, then the match type is just too restrictive and a typeclass becomes a more flexible / general / powerful technique.
So I believe it is worth to mention that. Which, again, is not to say that match types are always "worse" than typeclasses; I actually find them better for small / contained cases.
Legit argument.
4:37 Couldn’t this be achieved with typeclasses in Scala 2?
Not at this clarity - you'd need to wrap everything inside TC instances.
It may be even better to implement this with typeclasses since with match types all cases are stuffed into one function making addition of new cases problematic when that function comes from a library.
@@mykytal8991 It depends, if you are writing a library type-classes are probably a preferable solution, but when you are writing application, match type approach is safer (as far as I am aware if you mess-up pattern match branches you get compile time error) and a lot simpler.
Awesome set of videos. Keep up the good work. Your style of explanation is easy, concise and better than reading a blog. Must confess I hate that’s scala3 mix and matches indentation and brackets. I have to work in python 30% of the time and it’s the thing I dislike most stylistically of the language..
Glad you like the videos. As for Scala 3, just keep the old style, it will work just as fine.
This was some great coverage. Awesome video. Yours is one of the few channels that make me drop everything to watch whenever you post something.
As a side question: Do you think the lastComponentOf method would work better as a transparent inline method (return type being transparent at compile time based on an inlined match statemend therefore no match type needed) or do you think the match type would be a better way since it also provides some form of direct documentation?
Wow, such high praise, thank you!
Yes, I think match types are the best solution for this kind of problem. Sure, you can use inlining or other code expansion tools to get to the same result, but nothing comes close to the cleanliness of this one as far as I'm aware.
Wow wow! What a nice explanation!
Glad to hear it!
you're an absolute legend, thank you so much for all these quality videos!!
Glad they're useful! More incoming!
Nice video, but still need to explore more the usage. It seems it would help me in a Scala 2 project where I wanted to tie the type of a Question with the Type of the answer. There was no way to express this in types. Could be nice also for functions that translate between different ADT.
Thanks guys for the great lectures!:)
Glad it helped - although it's just me :D
Another helpful tutorial🤗
Many thanks
Keep it up💪
Glad it's useful!
Another great video. Thanks!
Glad it's useful!
❤️ ❤️ ❤️ ❤️ ❤️
The generics doesnt necessarily mean it has to be directly from the type argument. Both OOP and FP can use generics. The sample is simply type lambda that takes a type argument as a generic type.
The argument was not between OOP vs FP. It was purely from the perspective of how the compiler validates the types.
Nit: why s.charAt(s.length - 1). s.last works on strings. And last methods already throw NoSuchElementException, no need to check and rethrow.
Thanks! 👍
How can we write lastComponentOf so that it returns Either instead of throwing exceptions?
I was able to write accumulate in this manner:
def accumulate[T](accumulator: T, value: ConstituentPartOf[T]): T = (accumulator match
case b: BigInt => b + value.asInstanceOf[ConstituentPartOf[BigInt]]
case s: String => s + value.asInstanceOf[ConstituentPartOf[String]]
case l: List[_] => value.asInstanceOf[ConstituentPartOf[l.type]] :: l).asInstanceOf[T]
Not incredibly concise for the function author with all of the compiler hints, but I believe still type-safe as far as the user is concerned.
EDIT:
Turns out there is no real compiler safety here inside the function itself, as I was able to add this nonsensical case: `case d: Double => d + value.asInstanceOf[ConstituentPartOf[String]]`. But as the "library" user, you would not be able to call the function in a way to actually exercise that case.
about 10:35 - you said this is not possible in scala 2, but what about just implementing 3 methods instead of one case block? it's 100% compile time safe.
But it doesn't provide the same info, does it?
what about using method overloading in scala 2?
What about method overloading?
@@rockthejvm seems to be possible return different result for different argument in the same method
@@kotobotov Yep - something confusing?
can't you use GADTs for flow typing?
flow typing is limited
I like this solution. My only concern is that it doesn't seem extensible. Perhaps extensible match types will be part of scala 3.1?
What do you mean by extensible in this case?
@@rockthejvm I have the same concern. When you define the match type there's no way to extend it to new types (ConstituentPartOf is "closed") after it was defined. The "lastComponentOf" function is "closed" since I can't extend it without modifying the function itself (adding another case)
I'm not too familiar with Scala (mostly Haskell) but couldn't you get the same behavior using type classes? Defining something like "ConstituentPartOf[A,B] where lastComponentOf[A, B](src: A): B" (not sure about the syntax btw)
@@rockthejvm What if i want to extend a match type to match more distinct pair of types, but in a different file? How would you do that? Everything seems to require to be inside one big match statement
Great video, thanks! For me as a client of your awesome new lastComponentOf API, can I extend the API to other types? For example, suppose I want to be able to get lastComponentOf a SortedMap. Is there a way for me to add that functionality without modifying your library?
This use-case assumes you're offering the library under a single API, so if you want to support an additional type, you can add cases in the match type + implementation and your single API will still work.
@@rockthejvm I think this presumes that I have access to the library source code. I'm asking if there is any way to extend the type match if I DON'T have access to modify the library code. (I'm guessing the answer is no, that's what type classes are for?)
Another great video. Thanks!
Glad it helps!