Hello all! I am a (very) beginner Elm programmer, but I know a fair bit of Haskell. I listened to the most recent Elm Town podcast and was especially intrigued by the discussion around a novel use of record types to solve a real-world problem - embedding a nice CSS DSL directly into Elm. Awesome!
I was further intrigued by Evan's suggestion that type classes were in fact a poor solution to this problem. I'm well aware that type classes are an overused abstraction in Haskell that can cause lots of pain in unintended ways, especially when using them for the sole purpose of reusing nice, short function names. This is exactly what a CSS DSL must do, however - we'd like to be able to say both *"overflow: hidden" *and "border-style: hidden" in our DSL by reaching for the same intuitive name *hidden* and just have everything magically work. Sounds like exactly the difficult sort of modeling problem that type classes seem *superficially* suitable, but are often not in practice. However, after looking up the venerable elm-css <https://github.com/rtfeldman/elm-css> library and peeking around its internals, I'm not sure I agree. The implementation is, in a word, abstruse. The types that end-users have to see and interact with are highly non-intuitive. There's a lot of data being slogged around at runtime to satisfy the type checker. It's simply not a good abstraction. The DSL that came out on the other end looks great, but at what cost? For some context, here's how I'd proceed if I had to implement this in Haskell with type classes. Say we want to model the key *margin* which can have a value of *auto* or some integer (simplifying a bit here). I'd write the following type class: * class Margin a where* * margin :: a -> Style* Then, I'd write two instances for the type class - one for my made-up type *auto* and another for *Int*. * data Auto = Auto* * instance Margin Auto where* * margin Auto = {- implementation... -} instance Margin Int where margin n = {- implementation... -}* Here's what the final type of the *margin* combinator looks like: * margin :: Margin a => a -> Style* and I could use it like so: * margin Auto* * margin 5* Now, I'm not necessarily advocating this approach (DSL design is hard), but there it is, for reference. I happen to share a lot of the sentiments expressed in the podcast about type classes, but in this specific case, they actually don't seem too bad. Compare the above with the machinery in for margin that exists in elm-css: *type alias LengthOrAuto compatible =* * { compatible | value : String, lengthOrAuto : Compatible }* * margin : LengthOrAuto compatible -> Style* * auto :* * { lengthOrAuto : Compatible* * , overflow : Compatible* * , textRendering : Compatible* * , flexBasis : Compatible* * , lengthOrNumberOrAutoOrNoneOrContent : Compatible* * , alignItemsOrAuto : Compatible* * , justifyContentOrAuto : Compatible* * , cursor : Compatible* * , value : String* * , lengthOrAutoOrCoverOrContain : Compatible* * , intOrAuto : Compatible* * } * Compatible, what? Some value field is a String? Keep in mind these type are all visible to the user - necessarily so - so they can figure out how to fit the pieces together. However, I don't think it's unreasonable to say that the only way (or at least the *main *way) to use this library is to shut your brain off and trust that if you get any compiler error, it's because you did some illegal CSS thing. It's simultaneously exposes ugly innards (type aliases) and yet resists when you try to grok what's going on (Compatible is not exported, though for good reason). It concede it's possible I have overlooked some part of the library where the simple type class approach breaks down. But when attacked top-down (I want the DSL to come out looking like *this *so I need to use *these language features*), I'm not seeing why one would prefer this row-types approach over type classes if both happened to exist in Elm. Thanks for reading, and if this is the umpteenth post about type classes, I apologize. I figured at least the concept of type classes still relevant in this community, given they were briefly discussed on Elm Town just today. I'd love to hear any seasoned Elm-ers thoughts about row types as an abstraction mechanism, type classes or lack thereof, elm-css, or anything else that came to mind while reading over this post. Mitchell -- You received this message because you are subscribed to the Google Groups "Elm Discuss" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
