How likely are higher-rank types to be implemented sometime soon?

623 views
Skip to first unread message

Paul Chiusano

unread,
Apr 28, 2015, 12:03:44 PM4/28/15
to elm-d...@googlegroups.com
Hi all,

I asked about this back in the fall, and at the time Evan had more pressing things to do, but now I'm at a bit of a crossroads with my project so I wanted to raise the issue again and see what the latest thinking is. Elm's type system limitations have become a serious problem for me, to the point that I don't think I can continue using Elm. The features that I'd really need are:

* Support for type parameters whose kind is not *. An example is a type like `type Free f a = Pure a | Free (f (Free f a))`.
* Higher-rank types and/or typeclasses. I think Haskell 98 style typeclasses might be sufficient, but higher-rank types would let me fake typeclasses with value-level dictionaries and also be useful for other things. The last I heard from Evan, he wasn't too keen on just adding typeclasses but higher-rank types were not as controversial?

Just to clarify, I'm definitely not saying that just because I personally could use these features Evan should drop everything else and work on them. :) I would never presume such a thing! And Evan may feel these features are at odds with his goals for Elm, which is also definitely his call. I'm just trying to make an informed tech decision for my project which currently uses Elm, and having more info on if/when these features will be implemented will help a lot.

Cheers,
Paul :)

Joey Eremondi

unread,
Apr 28, 2015, 1:08:43 PM4/28/15
to elm-d...@googlegroups.com
I'm strongly in favour of "Support for type parameters whose kind is not *". I think it's a fairly simple feature to use, and would open up the way for generic programming.

I'll be honest, if there are higher-kinded parameters, I don't see where higher-rank types are overly useful. You can express pretty much everything that you normally would have a typeclass for using dictionaries. They solve a lot of problems and remove a lot of complexity (no need for FlexibleInstances, UndecidableInstances, etc. No monomorphism restriction, no ambiguity, instant support for multi-parameter classes).

Perhaps it would help the discussion if you gave some concrete examples of what you cannot write, and what specific problems it is necessary to solve. The solution might be the invention of the "Elm" way to do it, rather than changing the language itself.

--
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 elm-discuss...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Hassan Hayat

unread,
Apr 28, 2015, 1:10:57 PM4/28/15
to elm-d...@googlegroups.com
I'm with you when it comes to rank-n types. This is something that you don't think about when you are in JS or Python because with a dynamic type system you don't really care about these things, but when you're in Hindley-Milner world, it really starts to matter in surprising ways.

For example, one big win you get with rank-n types is the ability to pen down a "transducer" type (as in transducers in clojure). This'll make it possible to do loads of arbitrary operations on collections and combine these operations in all sorts of ways but you pay only one traversal of the collection. You can do it now, sort of, but it's not ideal. 

A random thing I just bumped in today how Haskell's QuickCheck is able to shrink and display functions. http://hackage.haskell.org/package/QuickCheck-2.4.2/docs/Test-QuickCheck-Function.html

Basically, you can test higher order functions and get a minimal failing input function that fails your property. This feature requires rank-n types. I'm thinking that I can get around it by dropping into Javascript, but I'm tired of dropping into Javascript because of the type system. It defeats the point. 

Type classes would be really cool and super useful but they're not indispensible. Like, it's possible to survive without them. But still, if that's on the table I'm all for it. Cuz, if we go back to the function shrinking thing, type classes allow us to print those things to the screen. Plus typeclasses will help Elm go towards the no runtime crashes thing. You can say that something is equatable or not. You can also extend stuff like "comparable" to your own types, which will make it possible to use compound types as keys to dictionaries and put your own types in sets (which is something the clojure folks take advantage of all the time).

But rank-n types are really a must. Whenever you see a library that defines some "phantom type" and then does a lot of its type magic in JS, chances are that should really be a rank-n type. If I had to pick just one language feature to add to Elm, rank-n types would be it (before higher kinds and typeclasses).

Basically, I like Elm and I'd like to write more Elm and less JS.


Now, on a more constructive note, how would we get the syntax for rank-n types to make it look less hard to learn?

type alias Reducer a r = r -> a -> r
type
alias Transducer a b = for all r in Reducer a r -> Reducer b r

and maybe comma seperate them if you have more than one:

for all a, b, c, d in ...

Paul Chiusano

unread,
Apr 28, 2015, 5:15:57 PM4/28/15
to elm-d...@googlegroups.com
> I'll be honest, if there are higher-kinded parameters, I don't see where higher-rank types are overly useful.

I was a little confused by your reply, but I am assuming you meant "I don't see where typeclasses are overly useful"? Meaning you don't see the need for typeclasses if there's higher-rank types + higher-kinded parameters, since you can do explicit dictionary passing instead, a la scrap your typeclasses?

Just to clarify the terminology - 

* Currently, in Elm, all type parameters have kind *, meaning they will be instantiated to types. So the type I gave, `type Free f a = Pure a | Free (f (Free f a))` is not possible to write, since it has a type parameter, `f`, which is being applied to another type to produce a type. The kind of `f` is `* -> *`, and `f`
* Higher-rank types, meaning a 'forall' appears to the left of an arrow in a type, as in `blah : (forall a . List a) -> ... `

> Perhaps it would help the discussion if you gave some concrete examples of what you cannot write, and what specific problems it is necessary to solve.

Part of the problem with this request is that the limitations (of not having any of the features I asked about) can always be worked around by duplicating code, and in bite-sized examples of the sort we could meaningfully discuss here, there won't be that much of it. So someone who is skeptical will often declare that this amount of code duplication is "no big deal"... but of course there can't be that much code duplication in a bite-sized example, so the skeptic is never satisfied! But for more realistic examples, the sort that are hard to discuss on a mailing list, there can be huge amounts of code duplication due to the lack of ability to do certain kinds of abstraction. It can go well beyond having to write `map2` N times for `Maybe`, `Result`, `Task`, etc, entire modules can start needing to be duplicated! This is roughly the position I am in.

Paul :)

John Mayer

unread,
Apr 28, 2015, 6:08:39 PM4/28/15
to elm-d...@googlegroups.com

Higher-kinded parameters might be a month or two away if it's prioritized or another contributor takes ownership. Either higher-rank types or traits/classes or something in that space is probably multiple months away, and similarly, this is a best case scenario that it is either prioritized or another contributor takes ownership.

Knowing that, your best bet is to wait for Evan to outline his goals for the next release, which he usually does a few weeks after the past release. Give him a chance to recover from jet lag! Asking in this manner is, frankly, stress inducing, not particularly productive, and never quite illuminating.

-OR-

I don't know what your project is like, if it's commercial or under contract or just a large personal project, but if you find yourself in the situation where you need a language feature, why not scratch your own itch and try to make the contribution?

John Mayer

unread,
Apr 28, 2015, 6:14:56 PM4/28/15
to elm-d...@googlegroups.com

(Those are probably conservative estimates. The right person might be able to knock out higher kinds in a weekend and higher rank in a month. Elm is open source. Such a person could make a temporary fork and prove the benefits)

Joey Eremondi

unread,
Apr 28, 2015, 6:21:09 PM4/28/15
to elm-d...@googlegroups.com

Sorry for the confusion.

What I'm saying is, if we have Type Constructors as parameters (HKP), shouldn't that together with records be enough to simulate most useful aspects of Typeclasses?

Then you can write something like:

type alias Mappable = {map : (a -> b) -> m a -> m b }

I don't see why we'd need to be able to move the foralls inwards, but I could be missing something from the theory where we would need that. There are some functions you could write more generally, but these seem like much more of a fringe case than, for example, having a "Comparable" typeclass.

I think what this ultimately comes down to is the generality vs. complexity tradeoff. Sometimes writing the most generic code isn't optimal, because you solve more than the problem at hand.

It's also important to think about the implications this has for type inference. "No type signatures ever needed" is a very nice promise, and to a non-academic, saying "we have a complex type system, and sometimes you need to figure out the signatures yourself" can be quite intimidating.

Hassan Hayat

unread,
Apr 28, 2015, 6:24:44 PM4/28/15
to elm-d...@googlegroups.com, john.p....@gmail.com
(Those are probably conservative estimates. The right person might be able to knock out higher kinds in a weekend and higher rank in a month. Elm is open source. Such a person could make a temporary fork and prove the benefits)

That made me wonder, can it be feasible to make up some reward for implementing a requested feature in Elm?

Like, I dunno, for college students, if you implement higher kinded types, Evan'll give you a recommendation which you can use and put in your resume for if you want to do some masters or go work somewhere or something.

I'm not very creative when it comes to these things, but I'm sure we can come up with some arbitrary rewards that have nothing to do with money. At least a point system or something small like tickets to a conference or whatever, just to encourage the practice. And then Evan can go to the issue tracker and put all these issues like "Implement typeclasses", "implement rank-n types" or whatever and put a label on it like "reward offered".

Just a thought...

Joey Eremondi

unread,
Apr 28, 2015, 6:27:53 PM4/28/15
to elm-d...@googlegroups.com
Actually, if you look at the typechecker, it already supports higher kinded types. There just isn't the syntax for it yet.

I know in previous discussions, Evan was hesitant about "fancy" types for Elm. So it's important to remember that "can we implement this" isn't the only question to ask here.

Paul Chiusano

unread,
Apr 28, 2015, 9:34:19 PM4/28/15
to elm-d...@googlegroups.com, john.p....@gmail.com
Ok, thanks for the reply. I will wait to hear from Evan, there is no rush. I didn't intend for my message to be stress-inducing or anything, was just asking what I think are reasonable questions.

Joey Eremondi

unread,
Apr 29, 2015, 6:23:12 PM4/29/15
to elm-d...@googlegroups.com
Sorry, I also didn't mean to call the discussion stress-inducing. I just think it would be bad if someone went through the effort implementing this, only to find that it won't get merged in...

Kim-Ee Yeoh

unread,
Apr 30, 2015, 3:09:32 AM4/30/15
to elm-d...@googlegroups.com
One of the mightiest writers of the 20th century wrote this to remind herself:

"Do stuff. Do not wait for society's kiss on your forehead."

If Evan had waited for that kiss, would we have Elm today?

Be like Evan.

Open source licenses restrict so little and grant so much, and they are so very under-utilized.

The bug bear in this case is not about the implementation, it's about the syntax.

Choose one, blaze the trail, and the community will be grateful that there's a concrete pilot on which to base even better decisions.

-- Kim-Ee

Evan Czaplicki

unread,
Apr 30, 2015, 8:01:26 AM4/30/15
to elm-d...@googlegroups.com
Paul, why do you need these features? Last time this came up, and this time too, no concrete examples were given of "here's a thing that is hard, here it is easier." When we talk about string-interpolation we had like 10 examples right out the gate.

If a feature is being suggested and there are no examples, no specific use cases, and no clear concrete benefits, it does not seem like a very convincing suggestion. For example, someone might say "I need nominal subtyping for my project". There's essentially nothing we can do with that statement except be skeptical. If they share an example or concrete issue, we can actually find a way around it or see that it does help with something.

We had a big discussion about special syntax for tasks that may require HKP and Rank2 depending on which route is taken, but I currently think elm-test, concurrency, that one fix for elm-package, native reviews, and animation are all more important than this. Please note, all of the things listed here solve concrete problems that are easy to demonstrate.

If you want to push for these things, please demonstrate the value!

--

Max Goldstein

unread,
Apr 30, 2015, 8:47:43 AM4/30/15
to elm-d...@googlegroups.com
I'll get behind Evan and say that for type system features, concrete examples are even more important. Having studied Haskell's various extensions on Hindley-Milner (type classes, GADTs, data kinds...), the distinction between what was and was not possible without the extension was always extremely subtle. Unlike string interpolation, with type system features it's much harder demonstrate value.

Think of people who play fast and loose with JavaScript -- demonstrate value to them. Write an explanation that fits in a paragraph and code snippet in a blog post, rather than an academic paper.

John Mayer

unread,
Apr 30, 2015, 8:50:43 AM4/30/15
to elm-d...@googlegroups.com

Well, from Paul's earlier post:

"Part of the problem with this request is that the limitations (of not having any of the features I asked about) can always be worked around by duplicating code,... there can be huge amounts of code duplication due to the lack of ability to do certain kinds of abstraction."

So I'm curious, which certain kinds of abstraction? Maybe rather than examples, you can share the patterns that you want to employ. And point us to blog posts about why they are valuable.

Paul Chiusano

unread,
Apr 30, 2015, 11:54:39 AM4/30/15
to elm-d...@googlegroups.com
> Why do you need these features?

Okay, I will sincerely try to answer that question. The basic reason is that without these features, the programmer is forced to duplicate code, sometimes lots of it.

I'll start with a simple example, the `map2` function, which is defined for lots of types:

-- Combine the results of two `Maybe` values, assuming both are `Just`
map2 : (a -> b -> c) -> Maybe a -> Maybe b -> Maybe c
map2 f a b = ...

-- Combine the results of two `Result` values, assuming both are `Ok`
map2 : (a -> b -> c) -> Result x a -> Result x b -> Result x c
map2 f a b = ...

It is well-known that `map2` can be implemented once and for all for any monad. An implementation is (hypothetical Elm syntax):

map2 : Monad f -> (a -> b -> c) -> f a -> f b -> f c
map2 m f a b = a `m.andThen` \a -> m.map (f a) b

So, at this point, rather than having N copies of `map2`, one for each type that forms a monad, we have a single implementation. Great! (And if you really want to also give a more "intuitive" name, you can still alias the function `map2` with some other name in the modules for specific types like `Maybe`)

The skeptic might object at this point - but what's the big deal, `map2` is like one line of code, who cares if I have to implement it over and over again for different types?? Or you might say that the definition is so short that users can just inline its definition whenever they need that operation.

The response to that is that we are going to be forced to duplicate *all* the functions that can be implemented generically for any monad, not just `map2`. Some of these definitions (like `sequence`) are nontrivial. And all these functions are useful because they encapsulate common patterns that occur over and over again in code, just like `map`, `filter`, `foldl`, and `foldr` encapsulate common patterns of iteration and sequence processing that occur all over the place. Without the needed type system features, we are talking about having N copies of an entire module of useful functions, one copy for each of the N data types in our codebase that forms a monad.

So the argument for why we want the ability to define `Monad` is the same as why we want `map` or `filter`---we get to avoid repeating ourselves, and the code becomes easier to read once you are familiar with the abstraction. Do you look at a call to `filter` and always mentally expand it to its definition? No, it forms a new semantic level you don't even need to unpack once you are used to it. It's the same with functions like `map2`. Maybe some abstractions are initially more "intuitive", but the bottom line is that if an abstraction lets you factor out patterns that occur over and over again in real code and avoid repeating yourself, then that abstraction has proved its worth.

I suppose everyone has a different tolerance for code duplication, and the amount of duplication will depend on what you are doing, but it can become really problematic. For my project, the thing that really tipped me over the edge was this example, which requires the ability to define types like `Functor`. It's the start of a library for what are called abstract binding trees (ABTs). I am using it in a compiler with several different types of syntax trees - terms, types, type declarations... an ABT library captures a lot of the commonality of these trees and lets you define many operations on them once and for all. In my actual project, my ABT library has gotten to 300 lines of code and I keep finding new operations to add to it as I go. It's great! In Elm, I'd be forced to duplicate this entire module N times, for each tree I am dealing with. And then there's all the other code that depends on these modules... much of that will need duplicating as well. I think the amount of duplication goes beyond what any reasonable developer would be comfortable with, once they've spotted the pattern.

Going back to your question, do you "need" pattern matching, `map` or `filter`? You know, why do we even have static types or parametric polymorphism? How about functions? Let's just do all our programming in assembly language! You can still accomplish all the same "practical tasks", right?? Okay, yes, I am being flippant, but think about it... what is the justification for all these nice language features? Well, with these features we can do the same stuff using a lot less code, and with code that is easier to understand and easier to get right. It's the same with what I'm asking for. Yet there was a time where people expressed skepticism about all these features. They probably demanded convincing "concrete examples" of why you'd need them and were quick to dismiss examples that didn't feel compelling enough to them.

Paul :)

PS: Someone claimed that these features already exists in the compiler but just need syntax. I do not think that is correct, but perhaps Evan or someone else who works on the compiler could clarify that point. 

Also, in regard to open source contributions, if I had limitless time and there were buy-in from Evan then it might be fun to work on adding these features to Elm, though I do think there's a higher barrier to contributing to a compiler (especially written by someone else) vs say writing a library. I definitely get the "if you want this, contribute!" mentality, but everyone has different constraints and interests and I don't think we should begrudge a person for not contributing!

And anyway, I am not *demanding* these things, I tried to give my reasoning for why I want these things because Evan asked, and my original message was just *asking* about these features because I am trying to decide what technology to use for my project going forward. I have no right to demand anything, I can only either try to contribute myself like has been suggested or use another technology that has what I think I need for my project. If the answer is "Evan remains unconvinced and it's unlikely to happen unless you contribute it yourself and engage in a protracted debate about the merits of the feature and what the syntax should be", well then that is fine, but I would probably decide at that point that my time is better spent elsewhere!
You received this message because you are subscribed to a topic in the Google Groups "Elm Discuss" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elm-discuss/EZ17hBTXr2I/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elm-discuss...@googlegroups.com.

Richard Feldman

unread,
Apr 30, 2015, 1:47:15 PM4/30/15
to elm-d...@googlegroups.com
When I enthusiastically explain Elm to non-FP programmers, I've often watched their faces scrunch up like they smelled a fart as they say "...am I going to have to learn monads?" I don't think flippancy is warranted for language features so reviled by newcomers.

If I understand it right, the motivating example is that you have multiple types of syntax trees, each of which has their own implementations of map, filter, etc. and you want to make a library that interfaces with all N implementations rather than N libraries for each implementation.

What I don't follow is the need for the bespoke map, filter, etc. implementations. ASTs are trees, which are data structures easily parameterized by type using existing Elm tools. Why does each flavor of AST in this library need a different map implementation, as opposed to a single implementation (say, in the ABT library) of type map : (a -> b) -> AbstractSyntaxTree a -> AbstractSyntaxTree b? Do they need different traversal implementations or something?

Eitan Chatav

unread,
Apr 30, 2015, 2:29:34 PM4/30/15
to elm-d...@googlegroups.com
Like it or not, Elm has monads. Perhaps elmers should eschew the word "monad" and go for "workflow" or "andThenable" :-) Rank n types are a different thing though. The question of implementing rank n types is a tradeoff. On the positive side, it would
* reduce some boilerplate
* encourage uniform, reusable APIs making it easier to learn a new library
* allow exploration of a better "typeclass" system (scrap your typeclasses)
On the negative side, it would
* complicate the type system
* complicate type inference
* potentially be an extra cognitive burden for beginners
I'd be in favor of rank n types, eventually. It seems like the missing half of Elm's beautiful record system.

Richard Feldman

unread,
Apr 30, 2015, 3:28:49 PM4/30/15
to elm-d...@googlegroups.com
Right now I can emphatically answer "no" to the question "will I have to learn monads?" without being even slightly disingenuous.

I can no longer do that if common things are built to share code using that abstraction, even if by a different name.

Sharing code is nice, but so is a simpler set of language primitives, and those two are at odds in this case. Time to implement is far from the only cost here.

Paul Chiusano

unread,
Apr 30, 2015, 4:24:10 PM4/30/15
to elm-d...@googlegroups.com
> What I don't follow is the need for the bespoke mapfilter, etc. implementations. ASTs are trees, which are data structures easily parameterized by type using existing Elm tools. Why does each flavor of AST in this library need a different map implementation, as opposed to a single implementation (say, in the ABT library) of type map : (a -> b) -> AbstractSyntaxTree a -> AbstractSyntaxTree b? Do they need different traversal implementations or something?

This is a reasonable question. The answer is that the ASTs have different sets of constructors or 'shape'. The term AST will have one set (with lambdas, function application, pattern matching, etc), the type AST will have another set, and the type declaration AST will have another set. The `f` type param in `ABT f` determines this set of constructors, and you can write code that captures commonalities in the different choices of `f` without dumping everything into a single type. Having one `AbstractSyntaxTree` type for all of them would mean giving up type safety - the types no longer enforce that trees are well-formed as types/terms/whatever, and when you are pattern matching on trees (which you tend to do a lot in a compiler) you have no idea if what you are getting is valid. So parameterizing on the 'shape' is important for the same reason you want `List a` rather than just `List`... the type info gives you extra information and help from the compiler in making sure you don't do something stupid. :)

Anyway, this is just one example, let's not dwell on it too much.

> I don't think flippancy is warranted for language features so reviled by newcomers.

All right, I was just being evocative to make a point, apologies if anyone is bothered by that!

> When I enthusiastically explain Elm to non-FP programmers, I've often watched their faces scrunch up like they smelled a fart as they say "...am I going to have to learn monads?"

You know, I think it is unfortunate that some people have such a negative reaction to something they have only vaguely heard about! Okay, yes, meet people where they are at, but don't pander to whatever resistance or mental barriers they might have. What I would say in that situation is: "You won't have to learn about them right away if you don't want... but there will come a time when you are excited to learn about things like monads because it will be apparent they solve real problems in your code! Trust me, it will be awesome, and you'll get to learn about all this amazing, useful stuff that will make you more productive as a developer!"

Seriously, all this powerful stuff is exciting. It lets us get more stuff done, with less code, more safety, etc. Why the negativity and skepticism? Evan has done a great job of conveying his enthusiasm and optimism for all these other great choices that Elm has made - purely functional programming, FRP, etc, all of which can be a tough sell. Now why not start applying that same enthusiasm and optimism to some of the other amazing things FP has to offer? Yet every time this topic comes up, I sense a lot of doubting, skepticism, and resistance. Luckily Evan didn't have this attitude when he went to write Elm in the first place, or he'd never have gotten anywhere!

Paul :)

--

Richard Feldman

unread,
Apr 30, 2015, 4:52:50 PM4/30/15
to elm-d...@googlegroups.com
Anyway, this is just one example, let's not dwell on it too much.

I actually think focusing on motivating examples is really important...that approach seems to have served Elm really well over time, and I think much of the nice experience of using Elm can be attributed to its design's emphasis on addressing specific pain points without introducing others.

Having one `AbstractSyntaxTree` type for all of them would mean giving up type safety - the types no longer enforce that trees are well-formed as types/terms/whatever, and when you are pattern matching on trees (which you tend to do a lot in a compiler) you have no idea if what you are getting is valid.

I see. So you could implement it using AbstractSyntaxTree a, and then there would not be a code duplication problem, but then if you have two functions called optimizeJavaScriptAST and optimizeElmAST, you cannot guarantee at compile time that (for example) the former only accepts AbstractSyntaxTree instances created using buildJavaScriptAST while the latter only accepts AbstractSyntaxTree instances created using buildElmAST...so you could accidentally pass a JavaScript AST instance to optimizeElmAST and the compiler would not complain?

Am I understanding that correctly?

Evan Czaplicki

unread,
Apr 30, 2015, 5:06:55 PM4/30/15
to elm-d...@googlegroups.com
Yeah, being example driven is key. "How can new people connect a language to their daily work if the users can't even do it?"

If Richard is correct, I'd like to point out the AST I use in the Elm compiler. It has a nice comment explaining how it works. Do you understand the idea it is using? Is your case more complex such that this will not work? If so, please be specific about why.

--
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 elm-discuss...@googlegroups.com.

Evan Czaplicki

unread,
Apr 30, 2015, 5:27:50 PM4/30/15
to elm-d...@googlegroups.com
To be a bit more specific, we take this general AST through many phases, shown here. In each phase we augment it with additional information or clean up certain things. One concrete benefit of this was: it rules out any bugs where names with a prime in them made it to JS.

Paul Chiusano

unread,
Apr 30, 2015, 6:38:13 PM4/30/15
to elm-d...@googlegroups.com
> ...so you could accidentally pass a JavaScript AST instance to optimizeElmAST and the compiler would not complain?

That is one example, yes. You also have no assurance when you are building these trees that you haven't mixed them up and done something silly like included a let binding in an AST that's supposed to be a Type, say. You also lose pattern matching coverage checking (once Elm adds that) when inspecting these trees. It's all very dynamically typed, too ugly to really consider IMO.

If Richard is correct, I'd like to point out the AST I use in the Elm compiler. It has a nice comment explaining how it works. Do you understand the idea it is using? Is your case more complex such that this will not work? If so, please be specific about why.

Yes, I get what you are doing there. But that is the AST for terms. You then have a separate AST for types... and probably another for type declarations... etc. You obviously don't have a single AST to represent all these very distinct concepts, that would be silly.

In the ABT library I linked to, the recursive structure of the trees is factored out, and the "base functor" `f` in `Term f` represents just a single level of the tree. Many operations can then be implemented generically, assuming different things about `f`, like that it is `Foldable` or `Functor`, or `Applicative`, or whatever. For instance, capture-avoiding variable substitution is shown there, but there are lots of other things. (checking for alpha equivalence, prettyprinting, JSON seralization, hashing...) It's great! And if you want, you can apply the same trick you do to your expression AST to allow the tree to be annotated, to track phases of compilation. Then it would become `Term f ann`... or maybe you could roll the annotation into the `f` parameter.

In any case, I feel this is getting pretty technical, so I would be happy to take this offline if you are interested...

I get that people want examples that are relevant to what they happen to be interested in doing with Elm, but I feel the point has been missed. The point is that there can often be (lots of) code duplication without these features. The argument I gave for that is very general, even though I picked just a few examples. But anyone making good use of these features can come up with their own compelling examples (for them) for whatever they happen to be doing. Are we going to pick apart each one? Does the example have to be Mario in the browser or the interface for a CRUD app for it to "count"? I don't really know what examples will be considered compelling or convincing by others. Evan, if you are interested, perhaps you could talk to others who also think these features are valuable and understand how they would or have made use of them. Maybe some of these examples will ring a bell with you.

Paul :)

You received this message because you are subscribed to a topic in the Google Groups "Elm Discuss" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elm-discuss/EZ17hBTXr2I/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elm-discuss...@googlegroups.com.

Max Goldstein

unread,
Apr 30, 2015, 7:13:45 PM4/30/15
to elm-d...@googlegroups.com
Does the example have to be Mario in the browser or the interface for a CRUD app for it to "count"?

Kind of, yes. There's no delicate way to put this, Paul, but you're not Elm's target audience. We're trying to attract JavaScript programmers, and they're not implementing trees or the lambda calculus. They don't use terms like

pattern matching coverage checking 
the "base functor" `f` in `Term f`
capture-avoiding variable substitution

These aren't bad words, it's just that Elm is trying to break out of the ivory tower. Go to a JS or Ruby meetup. Those people are our target audience. 

Richard Feldman

unread,
Apr 30, 2015, 7:25:18 PM4/30/15
to elm-d...@googlegroups.com
I get that people want examples that are relevant to what they happen to be interested in doing with Elm, but I feel the point has been missed. The point is that there can often be (lots of) code duplication without these features.

I'm not sure if this was directed at me, but I'm asking for clarification because I'm not a compiler author, and I'm trying to understand your example so that I can properly consider the pain point.

> ...so you could accidentally pass a JavaScript AST instance to optimizeElmAST and the compiler would not complain?

That is one example, yes. You also have no assurance when you are building these trees that you haven't mixed them up and done something silly like included a let binding in an AST that's supposed to be a Type, say. You also lose pattern matching coverage checking (once Elm adds that) when inspecting these trees. It's all very dynamically typed, too ugly to really consider IMO.

That's a fair personal preference to have, but everyone has different sensitivities when it comes to compile-time checking...others might consider Haskell too dynamically typed and ugly given that Idris exists. :)

I get that people want examples that are relevant to what they happen to be interested in doing with Elm, but I feel the point has been missed. The point is that there can often be (lots of) code duplication without these features. The argument I gave for that is very general, even though I picked just a few examples. But anyone making good use of these features can come up with their own compelling examples (for them) for whatever they happen to be doing.

The thing is, this discussion has revealed the opposite. We already established that this can be implemented without code duplication, at the cost of less compile-time safety. It's fine to argue "the extra compile-time safety is worth the added complexity," but an incremental compile-time safety improvement for a language that already has better compile-time safety than what 99.99% of industry programmers use in their day jobs is a far less convincing argument than the universal pain point of code duplication.

Paul Chiusano

unread,
Apr 30, 2015, 8:29:18 PM4/30/15
to elm-d...@googlegroups.com
Richard, I do think you're right that you can sometimes trade type safety for code duplication... in other cases I don't see that being possible, and in other cases perhaps it is technically possible but it isn't reasonable... then again I guess that is somewhat subjective. Sorry for not qualifying my statements appropriately though, I wasn't even thinking of that angle!

Max, I also think you are right. I guess I did have some hope that even if I'm not the "target market" for Elm I could still use it since it's a nice tool in many ways. Anyway, I'm glad for the work that Evan and others have done, even if Elm doesn't end up working out for my project.

In any case, it sounds like I have my answer here about whether any of these features are happening anytime soon, and I'm going to quit trying to convince anyone. :) I kind of feel like we could talk about this stuff for a long time making very similar arguments on both sides. But at a certain point, either you see these things as valuable or you just aren't sold.

Cheers,
Paul :)

PS: Evan feel free to ping me off-list if you want to discuss anything related to ABTs or internal compiler representation stuff. I know that was somewhat of a tangent but I'd be happy to talk about it if you want.

--

Max Goldstein

unread,
Apr 30, 2015, 8:45:48 PM4/30/15
to elm-d...@googlegroups.com
Max, I also think you are right. I guess I did have some hope that even if I'm not the "target market" for Elm I could still use it since it's a nice tool in many ways. Anyway, I'm glad for the work that Evan and others have done, even if Elm doesn't end up working out for my project.

Thanks Paul, you took that really well. 

Jeff Smits

unread,
Apr 30, 2015, 8:54:51 PM4/30/15
to elm-discuss
I think Paul just showed us how to have a great, calm discussion on a subject that's been hard to discuss on this mailing list for a while now. Thanks Paul, for your thoughtful and carefully phrased posts.
FWIW I'm with Paul about having these more advanced types, at least in the long run. But I won't get into it, because I'm really poor at giving motivating examples.

Richard Feldman

unread,
Apr 30, 2015, 9:08:20 PM4/30/15
to elm-d...@googlegroups.com
Also appreciate the civil tone...sometimes the system works! :)

All the best, Paul!

Paul Chiusano

unread,
May 1, 2015, 9:19:11 AM5/1/15
to elm-d...@googlegroups.com
Ok cool, thanks everyone.

Paul :)

Alex Neslusan

unread,
May 2, 2015, 8:10:32 AM5/2/15
to elm-d...@googlegroups.com
I strongly agree with this mindset. I wouldn't necessarily be against this proposal, but I think Evan should be very careful in adding things like this.
Reply all
Reply to author
Forward
0 new messages