5 ms·
> Would also be nice to know how they deal with boilerplate, which is a big annoyance with Elm in my opinion when you come from, eg, Haskell. We have over 55,0
by rtfeldman 10y ago
> Would also be nice to know how they deal with boilerplate, which is a big annoyance with Elm in my opinion when you come from, eg, Haskell.
We have over 55,000 lines of Elm code in production at http://noredink.com http://noredink.com and it's now the majority of our front-end. Students use our site to answer millions of questions per day, and they've answered over 2 billion questions total. I think this qualifies us as a "big project." ;)
Our Elm code is about as DRY as our JavaScript code, and adding language features to squeeze even more DRYness out of it is way down my priority list. When I think about ways I want Elm to improve, I'm thinking about better support for server-side rendering and asset management (e.g. code splitting), not wanting to add type system features.
I'm always a bit confused by "I tried Elm and disliked how it wasn't enough like Haskell" posts. PureScript is a fine language that's already on board with this design philosophy. Instead of lobbying for Elm to change, why not use PureScript? There's plenty enough room in the programming world for both languages to coexist!
- renlo 10y agoThe creator of Elm works for your company though [1]. Seems like your experience is going to be a lot different from the average developer because you can probably walk 20 steps to the designer of Elm to ask a question. I haven't used Elm just raising the point. [1] https://twitter.com/czaplic https://twitter.com/czaplic
- sotojuan 10y agoNot only does the creator of Elm work there but so do the people who know Elm more than anyone else in the world. Literally. While it's proof Elm can work it's not really a fair comparison for teams wanting to adopt it.
- rtfeldman 10y agoThat's true for me, yes, but the author of this article works at a totally different company halfway around the world. :)
- deleted 10y ago[deleted]
- vvanders 10y agoThat's only been a recent development, rtfeldman has been using Elm for noredink for a long while before that.
- rtfeldman 10y agoWe hired him because we'd already been using Elm for awhile and were so happy with it we wanted to contribute financially in addition to contributing with the libraries we open-source. :) My boss wrote more about it here: http://tech.noredink.com/post/136615783598/welcome-evan http://tech.noredink.com/post/136615783598/welcome-evan
- wyager 10y ago> Our Elm code is about as DRY as our JavaScript code Not to be flippant, but that's not saying much.
- the_gipsy 10y agoJavaScript code is or can be extremely DRY. I have noticed this especially on a redux (inspired by Elm's architecture) codebase. The pitfall is that JavaScript is very unsafe and hard to read. I've worked with TypeScript too, and there I started to notice more repitition and boilerplate, although IMO It is very well worth the safety.
- the_duke 10y agoJavascript is dynamically typed though. And Typescript has generics or circumventing the type system with :any to enable reusability.
- the_gipsy 10y agoExactly my point, dynamically typed means writing less boilerplate. At the cost of safeness and mantainability. TypeScript's any is more for compatibility with js libs though.
- boubiyeah 10y agoYes, in a sane codebase, you (almost) never use <any>. 98% of the time, if you type <any>, you're being lazy.
- leshow 10y agoYour response to all the criticisms about boilerplate is basically sticking your fingers in your ears and going "nananana". Elm code is boilerplate heavy, it's a trade off you guys have made to make the language simple. That's okay, but don't pretend like you didn't make the trade off.
- rtfeldman 10y ago> Elm code is boilerplate heavy, it's a trade off you guys have made to make the language simple. I just straight-up disagree with this. If Elm added Haskell-style typeclasses today, and we refactored our whole code base at work to use them, I bet it would save us less than 1% LoC. This is such a weird thing to try to respond to. It's like a group of people started insisting that JavaScript's big problem is all the parentheses, and accusing JavaScript developers of sticking their fingers in their ears about the most obviously glaring weakness in the language. What reasonable response can anyone make to that claim, except "I really don't think that's what the language should focus on improving?"
- iopq 10y agoYes, that's true, but I bet you some of the functionality that you wrote can be factored out into a more generic library that applies to everyone's use case. So it could be that half of your code is "library" code that could be open sourced - if it could be written generically with typeclasses. For example, Rust has typeclasses (called traits) and Rust has a lot of libraries that do the right thing for you. fn accumulate<'a, T: Monoid>(tuples: &[(&'a str, &Fn(i32) -> bool)], i: i32) -> Option<T> where T: From<&'a str> { tuples.iter() .filter(apply(second, i)) .map(first) .cloned() .map(<&str>::into) .fold1(T::op) //op just concatenates, but Cow<'a, str> does not satisfy Add } Here, the functions first and second are taken from tool.rs, fold1 is from itertools, the type Monoid and the function op is taken from monoid. I did have to write the delayed apply function myself, though. It's definition is: fn apply<A, B, C, F, G>(mut f: F, a: A) -> impl FnMut(&B) -> C // must still be `for<'r> impl FnMut(&'r B) -> C`, because that’s what filter requires where F: FnMut(B) -> G, // must not be `for<'r> FnMut(&'r B) -> G`, because regular functions do not implement it G: FnMut(A) -> C, B: Copy, // for dereferencing A: Clone { move |b| f(*b)(a.clone()) // this must do any bridging necessary to satisfy the requirements } I think that could also be in a library somewhere
- the_duke 10y agoMy major question is: how do you write reusable code without type classes? It's not really possible, as far as I can tell.
- kod 10y agoAre you trying to say that in the 30 years of time between the discovery of Lisp and the discovery of typeclasses, no one wrote reusable code?
- thinkpad20 10y agoWell of course there are ways to write reusable code without type classes. But let's say for the sake of argument that that's really what you want. You can in fact implement ad-hoc polymorphism, or something very close to it, without type classes. Type classes can be seen as a way to implicitly add a set of values to scope, but this can be done explicitly by using a record type. Consider (I'm guessing at syntax, I don't know elm): -- Monoid as a record type type Monoid a = { empty: a, append: a -> a -> a } -- Using this to make a generic concatenator mconcat : Monoid a -> [a] -> a mconcat m = foldl m.append m.empty -- Instance of Monoid for strings MString : Monoid String MString = { empty = "", append = (++) } -- Instance for ints MInt : Monoid Int MInt = { empty = 0, append = (+) } -- Use the generic function unlines : [String] -> String unlines = mconcat MString . intercalate "\n" You'll note here that the actual function signature for mconcat looks almost exactly like its Haskell counterpart, just that the Monoid instance is a regular argument like any other. Of course the downside is that you have to pass in the instance argument to any "type class polymorphic" function, so you lose a bit of conciseness but not much in the way of expressiveness. And you actually gain a bit, because instances are explicit (for example, you could make an instance of Monoid Int which used 1 and (*) instead). I'm not sure if this pattern is used anywhere in extant Elm code, but this is a common way to implement type classes under the hood in languages that support them, and the approach seems doable in elm.
- rymohr 10y agoIs that 55,000 lines of elm in a single large project or across all projects?
- rtfeldman 10y agoA single large project. We've also split out several pieces of it into libraries that we've open-sourced, but I'm not counting those.
- b123400 10y agoYou definitely have access to something we don't, and that might changes how you view Elm. For example a normal Elm user can only send/receive values to/from JS via ports, and that is not scalable, e.g. you are not suppose to use ports in library. But at NoRedInk I guess you have some sort of native module wrapper (https://github.com/NoRedInk/take-home/wiki/Writing-your-first-Elm-Native-module https://github.com/NoRedInk/take-home/wiki/Writing-your-firs...) that we cannot access?
- elbear 10y agoAnyone can write a native module. You can't publish it, but you can use it in your project.[1] [1] http://faq.elm-community.org/#why-doesnt-the-elm-compiler-find-the-native-code-in-a-module-that-i-cloned-from-github http://faq.elm-community.org/#why-doesnt-the-elm-compiler-fi...
- lgas 10y agoNative modules are accessible to anyone. Unfortunately the link you provided is a little out of date despite being the top hit on google. Here is an example of a native module that works with 0.17 though: https://github.com/evancz/elm-markdown/blob/master/src/Native/Markdown.js https://github.com/evancz/elm-markdown/blob/master/src/Nativ...
- b123400 10y agoYeah I figured the "rules" to write a native module for a while, but there is no tutorial, no support at all, which makes me rather uncomfortable, and I am never confident with that code.