-
-
Save JoeyEremondi/26de88df1cadbc5347d9 to your computer and use it in GitHub Desktop.
-- interface is a more familiar name for the intention of the construct | |
type alias Append a rest = { rest | append : a -> a -> a} | |
-- implement is a more familiar name | |
stringAppend rest = {rest | append = \x y -> x ++ y} | |
-- this is Monoid | |
type alias AppendWithId a rest = Append a {rest | id : a} | |
-- ^ 1 potential superclass | |
appendWithIdString rest = {rest | id = ""} | |
type alias Eq a rest = | |
{rest | eq : a -> a -> Bool} | |
neq : Eq a rest -> a -> a -> Bool | |
neq inst x y = not <| inst.eq x y | |
concat : AppendWithId a rest -> List a -> a | |
concat inst list = | |
case list of | |
(x::xs) -> | |
inst.append x (concat inst xs) | |
[] -> | |
inst.id | |
foo : Append a rest -> Eq a rest -> a -> a -> a | |
foo ainst einst x y = | |
if neq einst x y then | |
ainst.append x y | |
else | |
x |
True, but I stand by my opinion that HKP and Typeclasses are completely orthogonal. That you can add HKP to get that kind of generics without having any special typeclass syntax.
I'll take HKP, just so long as there is a way. Since HKP would be nice in more contexts, is a smaller departure, and is a smaller feature, HKP first sounds nice and conservative. Then a more informed estimation is possible, after seeing how dictionary passing works in practice. Is there a way I can lobby for HKP as a feature?
Basically, any lobbying should be rooted in use cases. What, specifically, is not having HKP stopping you from producing in Elm? Or, what negative artifacts / antipatterns are showing up in your code because of it?
Evan definitely won't integrate it just because people think it's a cool feature.
HKP and Typeclasses are completely orthogonal
I never said they weren't. Indeed, with HKP we could make these not-typeclasses records work.
What, specifically, is not having HKP stopping you from producing in Elm?
Something along these lines:
type alias Chainable m rest =
{ rest |
constant : a -> m a,
map : (a -> b) -> m a -> m b,
andMap : m (a -> b) -> m a -> m b,
andThen : (a -> m b) -> m a -> m b
}
sequence : Chainable m rest -> m b -> m a -> m b
sequence {andThen} after before =
andThen (\_ -> after) before
map2 : Chainable m rest -> (a -> b -> c) -> m a -> m b -> m c
map2 {map, andMap} f a b =
map f a `andMap` b
As a reminder, the real benefit of type classes is not generic code. We have most of that already thanks to consistent naming across libraries. The benefit is being able to take a small API and build many more useful things on top of it. For example, anything that can be compared can support min
and max
, and a list of comparable things is sortable.
Is the ability to generalize functions over Monads not a sufficient use case?
Monads for monads' sake don't count for anything. See Joey's comment: what can't you build? How is your code adversely affected? Granted, my example isn't really concrete either.
I guess I feel differently, in that monads for monads sake is useful. One of the appeals of working in a functional languages is the many problems are solved for you by useful functions, and reduce cognitive overhead by using simple polymorphic abstractions. sequence
is a great example, if we had sequence
I would not have to write it myself so frequently and for different types. I can do it because I understand this one very well, but I'd not claim that for all the functions I regularly use, nor would I want to demand that level of understanding from others, simply to take advantage of useful abstractions like sequence
or andMap
.
I guess I feel differently, in that monads for monads sake is useful.
This is a complaint as old as Elm itself. I would recommend reading this post from Evan on this subject, and maybe some surrounding context in that thread.
But doesn't this suffer from the lack of higher-kinded polymorphism? The following isn't valid:
And that's just the beginning if you want to write
map
,andMap
, andandThen
.