Settings

Theme

Elm in Production: 25K Lines Later

charukiewi.cz

449 points by charukiewicz 8 years ago · 277 comments

Reader

ToJans 8 years ago

First:

> Elm has an incredibly powerful type system

Near the end of the article:

>Want to decode some JSON? Hard, especially if the JSON is heavily nested and it must be decoded to custom types defined in your application.

IMHO the lack of typeclasses/traits is really hurting Elm. Take haskell f.e.

  {-# LANGUAGE DeriveGeneric #-}
  
  import GHC.Generics
  
  data Person = Person {
        name :: Text
      , age  :: Int
      } deriving (Generic, Show)
  
  instance ToJSON Person
  instance FromJSON Person
While I understand Evan's aversion against complexity, it makes me a bit wary about using ElmLang in production. I am currently using TypeScript, but if I would need a more powerful type system, I would probably switch to Haskell/PureScript or OCaml/BuckleScript instead.
  • _gi12 8 years ago

    I really wish people would stop spreading the meme that decoding JSON in Elm is "hard". Yes, Haskell allows you to automatically decode/encode datatypes, but this only works in the simplest of cases. For example, if your backend returns a JSON object with snake-cased fields, but your model has camel-cased fields, `instance ToJSON Person` won't work; you'll have to write a custom decoder. The automatic decoders/encoders in Haskell only work if the shape of your JSON perfectly matches your record definition.

    Writing decoders in Elm is not hard. It's manual. It's explicit. It forces you to specify what should happen if the JSON object has missing fields, incorrect types, or is otherwise malformed. There's a slight learning curve and it can be time consuming at first, but it guarantees that your application won't blow up at runtime because of some bad data. Because of this, JSON decoding is frankly one of my favorite parts about Elm.

    Typescript, on the other hand, offers no such guarantee. If you write a function that takes an Int and you accidentally pass it a String from a JSON response, your app will blow up and there's the nothing the compiler can do to help you. Personally, I'd rather write JSON decoders than have my app blow up because of a silly mistake.

    • charukiewiczOP 8 years ago

      Author here. I agree with your points, and in my article I specifically mention that there are benefits to some of the "hardness" of certain tasks in Elm (type-safety in the case of JSON decoding).

      But to claim that JSON decoding in Elm is not significantly more difficult than it is in JavaScript would be misleading. A front end developer that has only written JS will be surprised when he/she cannot just drop in the equivalent of JSON.parse() and get an Elm value out of it. I call it "hard" because there is a bit of a learning curve, and it does require some thought, and quite frankly it takes quite a bit of time if you have a large application like we do.

      Moreover, I am not complaining. And I do not think people should be. As I said in the article, the tradeoff is worth it.

    • somenewacc 8 years ago

      You don't need to write the decoder boilerplate manually to get all those benefits.

      For example, here's how you rename a JSON field while serializing/deserializing a data type in Rust:

      https://play.rust-lang.org/?gist=1b382bc1572858841d5e392435d...

      You just annotate the field with #[serde(rename = "..")]. Here is a list of such annotations

      https://serde.rs/field-attrs.html

      Serde is also generic in the serialization format; the example I linked uses serde_json, and was adapted from its README here https://github.com/serde-rs/json

      • MaxGabriel 8 years ago

        Same for Haskell. This package provides common translations like snake_case to CamelCase:

        https://www.stackage.org/haddock/lts-9.0/aeson-casing-0.1.0....

        Giving you automatic encoders/decoders like so:

        instance ToJSON Person where toJSON = genericToJSON $ aesonPrefix snakeCase instance FromJSON Person where parseJSON = genericParseJSON $ aesonPrefix snakeCase

        And the implementation of that package is like 4 simple lines for snake case; it's totally doable on your own for whatever you need https://github.com/AndrewRademacher/aeson-casing/blob/260d18...

        I haven't had to do snake_case to CamelCase with Aeson before, but I have dropped a prefix before, like "userName" -> "name", "userAge" -> "age", and it was pretty easy and well supported.

        Also I would note that this isn't as big of a deal for Haskell and Rust, because they're primarily backend languages, so they're more often sending out JSON in whatever form they please, rather than consuming it. In my experience the main consumers (Javascript on the web, Objective-C on iOS and Java on Android) use CamelCase anyway, so there's a natural compatibility.

    • empath75 8 years ago

      > Writing decoders in Elm is not hard.

      At some point, after enough people tell you that they can't figure out how to do something, one should admit that it is difficult.

      • Skinney 8 years ago

        Or that the documentation can be improved, or the API could be improved, or a number of other things. But really, JSON encoding/decoding in Elm boils down to specifying what the type of an object's field is. If that is difficult, interfaces and classes must be a nightmare.

        • empath75 8 years ago

          Here is what people are comparing it to, in terms of difficulty:

          json.loads('foo.json')

          I spent 3 days about 6 months ago, off and on, trying to figure out json decoding in elm, and gave up on the language entirely. It wasn't a matter of simply specifying types. The syntax is confusing the compiler isn't any help. I had very little difficulty working with anything else in Elm, so I think there is a lot of work to be done there.

          If there were a more painless way to do it, there are a lot of api's I'd love to write front ends for in elm.

          • fbonetti 8 years ago

            Elm's primary selling point is "no runtime exceptions". The subheader on Elm's homepage even says this[1]:

                Generate JavaScript with great performance and no runtime exceptions.
            
            Type safety doesn't come for free. If you want to build an application that doesn't have runtime exceptions, you have to specify what external data should look like, how to translate that data into a native datatype, and what to do if that expectation fails. With this in mind, you can't just write `JSON.parse(data)` and expect everything to work. What if your data has a different shape? What if it's missing fields? What if field "foo" is a String instead of an Int?

            Here's a short example in Typescript:

                import fetch from "node-fetch"
            
                interface Post {
                  user_id: number,
                  id: number,
                  title: string,
                  body: string
                }
            
                const postUrl = 'https://jsonplaceholder.typicode.com/posts/1'
            
                const show = function(post: Post) {
                  console.log(post.user_id)
                  console.log(post.id)
                  console.log(post.title)
                  console.log(post.body)
                }
            
                function getJSON(): Promise<Post> {
                  return fetch(postUrl).then(x => x.json())
                }
            
                getJSON().then(show)
            
            Do you see the bug? `user_id` is supposed to be `userId`, yet this example will compile without an issue. When you run it, `post.user_id` will be undefined, even though our interface clearly states that a Post most have a `user_id` field of type `number`. The compiler won't catch this even with `--strictNullChecks` enabled.

            Everything in programming is about tradeoffs. If you don't care about strong type correctness, use something like Typescript. If you do care, Elm is a great choice.

            [1]: http://elm-lang.org/

            • hudon 8 years ago

              Elm's type system will not catch this bug at compile time, it will still be a runtime exception, unless you're saying Elm's compiler does an HTTP call to that endpoint to verify that the interface matches the response object??

              The only difference in your example probably is that Elm will force you to handle the case where your interface doesn't match the response object (missing field, in this case). But nothing in Elm forces you to handle it well, so if the programmer forgot to handle it well in TypeScript, they will do the same in Elm, and the end result will be the exact same: a runtime failure.

              • Skinney 8 years ago

                > ... it will still be a runtime exception ...

                No.

                > The only difference in your example probably is that Elm will force you to handle the case where your interface doesn't match the response object

                Which means that no exception have been thrown. You've handled it, however poorly, and so you've made a conscious decision about how to deal with it.

                In TypeScript, this is much easier to forget, because you don't have a compiler that forces you to deal with it. If you're lucky, this causes a runtime exception at the place the error occurs. More likely, however, no exception is raised, and you get an error down the line which is hard to notice and hard to debug.

                Like, there's nothing in TypeScript that forces the developer to use the correct types. A lazy developer could just use "any" everywhere. So if the programmer doesn't handle this well in JavaScript, they will do the same in TypeScript, and the end result will be the exact same: a runtime failure. This, however, doesn't mean that TypeScript isn't a great tool that helps developers avoid runtime errors, it just means that this particular programmer doesn't take advantage of the tools at his/her disposal.

                • hudon 8 years ago

                  I agree that Elm is more strict at compile-time than TypeScript, there is no argument there.

                  What I have a problem with is using an example where the programmer is lazy in TypeScript, then assuming the programmer is NOT lazy when coding in Elm.

                  If you just print an empty error string and your app continues running assuming it decoded successfully, you can be in as bad a place as the app that crashed. Actually, in some mission critical cases, failing hard is safer than continuing with corrupt data.

                  In both cases, TypeScript AND Elm, you must do work to handle the failure to decode gracefully, it will actually be less work in TypeScript because you have less to specify. The only difference is that in Elm, you are reminded more often to do that work.

                  • Skinney 8 years ago

                    The difference here, is that a lazy programmer, when forced to handle this particular case, would use "Debug.crash 'should not happen'", which causes the Elm application to crash at runtime. The TypeScript program doesn't force you to deal with it, so the lazy programmer can assume everything works, no exception is raised, and you're now running with invalid state.

                    To catch this bug in TypeScript, you'd have to write runtime validation, which you are less likely to do, since you can just do JSON.parse. Elm, again, forces you to write runtime type validation, and handle the potential error (which could be something simple as crashing the entire application).

                    When it comes to reliability, that makes all the difference.

                    And I'm not arguing that Elm is great because of poor developers either. I do theese sort of things when I write typescript, simply because I just don't think about the error case that often. Elm is a wonderful straight jacket that makes me a better developer <3

                    • hudon 8 years ago

                      > Elm, again, forces you to write runtime type validation

                      But it does not force you to handle failures correctly (as your lazy programmer example proves). Forcing you to handle a Left value from an Either type is not the same as forcing you to handle a Left value correctly.

                      Elm doesn't save you from bad failure handling, which is what the post I replied to claimed.

                      What Elm does do is remind you that you should handle failures, which I think is very valuable and I'm not arguing against (and is what you're arguing for). But you still need to write a correct handler, and Elm won't force you to do that!

                      • Skinney 8 years ago

                        The original post did not make that claim. It said Elm made you handle errors where they can happen. It said nothing about handling it well. Typescript will not remind you every where an error will happen, and so it is very likely that you do not handle every error, even if it is your intention to do so.

              • fbonetti 8 years ago

                > Elm's type system will not catch this bug at compile time

                This is completely false. Elm's type system will force you to handle the decoding failure at compile time. The `decodeString` function has the following type:

                    decodeString : Decoder a -> String -> Result String a
                
                Which means that when you call `decodeString`, it will return a Result containing either the decoded value or a String describing the error.

                This is not a "runtime failure". This is the compiler forcing you to explicitly spell out what you want to happen the case of a decoding problem. The difference between Typescript and Elm is that Typescript's compiler will happily let your program crash when this type of issue occurs, whereas Elm's compiler won't.

                • hudon 8 years ago

                  You're just rewording what I said. The coder can simply ignore that string or make it empty, which can lead to a runtime failure if another module depends on a value being returned (think a runtime failure from a non-exhaustive pattern match). If they were a lazy coder in TypeScript, they'll be lazy in Elm (:

                  So, no, what I said is not "completely false"

                  • Exolon 8 years ago

                    I would dispute the idea that "if they were a lazy coder in TypeScript, they'll be lazy in Elm". The fact that Elm forces you to at least think for a moment about how handle the failure case is a good thing. Even if you're inclined to take the path of least resistance, as probably most of us programmers are, what does that actually look like?

                    You have one case branch that looks like:

                      GotUserList (Ok userList) -> ...
                    
                    and another that looks like:

                      GotUserList (Err _) -> ...
                    
                    Since we need to pattern match on both types of the Result structure in our update cycle, there isn't really an opportunity to implicitly gloss over a failure to parse. Of course you can try to make the decoder super permissive, but this is harder than doing it the right way. If you're REALLY lazy, you can throw in "Debug.crash" in the failure case, but there's no general solution for throwing grenades at your foot :D
          • Skinney 8 years ago

            json.loads('foo.json') (what is that? python?) requires that the json matches your types exactelly. This, at least for me, is rarely the case.

            And yes, it is. Here is code from my own project:

                Json.map3 HintTemplate
                    (Json.field "ID" Json.int)
                    (Json.field "Text" Json.string)
                    (Json.field "Image" Json.string)
            
            Where HintTemplate is a record type containing an integer id, a string text and a image url. What about this is difficult?
            • Tarean 8 years ago

              That's reasonably similar to handwritten haskell json parsing:

                  instance FromJson HintTemplate where
                      parseJson = withObject $ \o ->
                         HintTemplate
                           <$> o .: "ID"
                           <*> o .: "Text"
                           <*> o .:  "Image"
              
              I wrote <$> and <> out but there is a definition like map3:

                  liftA3 f a b = f <$> a <*> b
              
              This works for all applicatives, though. Is it normal for elm libraries to copy paste code like the map3 definition?
              • Skinney 8 years ago

                Not sure what you mean. It's normal that different packages have their own version of a given function, like List.map3, Json.map3 etc. But the implementation is different, so it's not copy-paste. It has to be this way in cases where generic types is not enough and something like interfaces/typeclasses is required.

    • wraithm112 8 years ago

      With respect to the snake-cased fields issue, it's actually not that hard to do that with Generic in Haskell.

        data Person = Person
             { personFirstName :: Text
             , personLastName  :: Text
             } deriving (Generic)
      
        instance ToJSON Person where
           toJSON = genericToJSON $ aesonPrefix snakeCase
        instance FromJSON Person where
           parseJSON = genericParseJSON $ aesonPrefix snakeCase
      
      Which produces messages like:

        {
         "first_name": "John",
         "last_name": "Doe"
        }
    • neerajdotname2 8 years ago

      I found it hard. I have been learning Elm for a few weeks. I have a background in Ruby and in JavaScript. I have a repo where I am trying difference cases. https://github.com/bigbinary/road-to-elm

    • KirinDave 8 years ago

      It's not so much that writing decoders is hard it's that writing robust parsers without applicative is quite laborious, once you're used to having them.

  • justinw2 8 years ago

    It seems like there's no solutions for generic JSON encoding for OCaml/BuckleScript/Reason either though.

    I've worked on various ways to do JSON encoding in Purescript through datatype generics, but the recent RowToList machinery lets us use record types directly. I have a post and some links collected if anyone is interested:

    https://www.reddit.com/r/purescript/comments/6mss5o/new_in_p...

    http://qiita.com/kimagure/items/d8a0681ae05b605c5abe

    • shakna 8 years ago

      Yojson [0] provides a generic way to read JSON in OCaml. Mostly by pre-defining a type that contains any valid json value.

      [0] https://github.com/mjambon/yojson

      • justinw2 8 years ago

        Right, there are some various approaches but I've had a few OCaml users tell me there were some reasons why they weren't/couldn't be used on the front end, sadly. Would really like to see some demos otherwise though

    • 3pt14159 8 years ago

      The only JSON encoder / decoder / manipulator that I've seen that is typed and reasonably flexible is Nim's, but even there it's still 5 times more complicated than say Ruby.

  • dmjio 8 years ago

    If you want a powerful type system (i.e. Haskell), but the benefits of Elm, Miso is a project that implements the Elm Architecture in Haskell. It obviously has typeclasses, and can encode / decode JSON on the frontend using GHC.Generics quite well. https://github.com/dmjio/miso

    • masklinn 8 years ago

      There's also Purescript, which is built for the web but has many of the goodies you'd expect from Haskell.

      • kahnpro 8 years ago

        I tried out Purescript for a project and didn't think it's ready for production... Generic encoding/decoding was painful, the lack of good dependency management was painful, too many things were either missing or immature. I hope in a year or two the story will be better.

        • KirinDave 8 years ago

          PureScript is a rapidly moving target. When did you try?

          • reitanqild 8 years ago

            Eh, I don't know PureScript but IMO you just added another reason why it isn't production ready.

            Unless they are really really good at keeping backwards compability in both language and build tools.

            • KirinDave 8 years ago

              Nope, they break things all the time. Or at least, they did one major break earlier this year. "Production ready" is different from "should I use this ever at all though."

              Doesn't really answer the question I posed though.

    • agentultra 8 years ago

      And GHCJS is quite good these days as well.

  • Rotareti 8 years ago

    Whenever something about elm pops up, I see people complaining about this. Reminds me of people complaining about Golang missing feature X. Still both languages raising in popularity. I haven't used Elm or Golang, but I'm almost convinced that simplicity is a driving factor for their success.

    • BoiledCabbage 8 years ago

      This is exactly it. I can't speak for Golang, but why Elm wins is its simplicity.

      You can go through the tutorial and know the language in two days. Seriously if you ever want a weekend project, try learning elm.

      Even if you don't want to use it, but just to be exposed to the ideas.

      You csn be writing productive code in a week. If anyone tell you Haskell (or Purescript) is less than months to be productive is not being honest about what production level productivity entails. You can learn the fundamentals in less time, but the prized significantly more complex abstractions makes ramping up significantly slower. And this is coming from someone who likes haskell and is enjoying learning the language.

      As someone who has used a lot of languages over the years, I really do say spend a weekend trying it out. The language has great ideas and a great philosophy.

      https://www.elm-tutorial.org/en/

      • Rotareti 8 years ago

        I'm playing with the idea of starting a project in Elm for a while now. I already built my own crippled version of Elm using TypeScript, React, ImmutableJS, Recompose and Ramda... The only thing holding me back is the ecosystem around JS, say ReactNative, d3, etc.

  • kccqzy 8 years ago

    That doesn't handle custom JSON schemas at all. I don't know much about Elm but at work in Haskell we mostly write FromJSON instances manually. It's probably the same amount of code as the data type definition.

    • ToJans 8 years ago

      For custom JSON schemas nothing tops F#'s type providers [0] IMO.

      [0] https://github.com/jet/JsonSchemaProvider

      • jlouis 8 years ago

        This is the way to go, if you are tied to JSON for some reason. A far better solution is to get rid of JSON if most of your stack is a typed stack and then convert to JSON as late as possible.

        Custom JSON which people invent as they go along is just going to present your system with pain over time.

    • pyrale 8 years ago

      Do you have examples of things that don't fit the vanilla FromJSON ? From the top of my head, there's lensed things (which can easily be done once with a LensJSON typeclass), and things which use a different serialization (e.g. no "tag" field, etc) for which there is little you can do, if each of your data types has its own standard. If they do, though, you could easily define a typeclass for each source (e.g. StackoverflowJSON, HackernewsJSON, etc).

      • kccqzy 8 years ago

        We don't do type classes for this purpose. If you have to interface with some JSON with predefined schema, we just write the instances manually. It's not that hard at all—usually it's just calling withObject with a series of lifted function applications `Constructor <$> (o .: "field1") <*> (o .: "field2")`. Remember the Parser type is a Functor/Applicative/Monad/Alternative/MonadPlus so there's a whole host of utilities for these classes that make writing such instances both simple and concise. Of course if you are doing simple things like removing the leading underscore on lenses data typed, just use TH to derive the instance, passing slightly modified `Options`.

        If you need to manually handle tags, here's a snippet that can help you:

            -- | Safely accesses a JSON object where the value at a key is text. It takes an
            -- object, a key, and a continuation of what to do when this key is present.
            --
            -- Example:
            --
            -- @
            --     data D = A | B | C Int
            --     instance FromJSON D where
            --       parseJSON = withObject "D" $ \o ->
            --         o .:=> "tag" $ \case
            --           "A" -> pure A
            --           "B" -> pure B
            --           "C" -> C <$> (o .: "theInt")
            --           t -> fail $ "Unrecognized tag in type D: " ++ unpack t
            -- @
            (.:=>) :: Object -> Text -> (Text -> Parser a) -> Parser a
            o .:=> k = \m -> o .: k >>= withText ("Object with mandatory key " <> unpack k) m
    • danidiaz 8 years ago

      One could use a generics library to emulate something like Go's struct field tags, which decouple "external" fields names from the actual field names. Something like this is explained from 48:50 in this talk: https://skillsmatter.com/skillscasts/10181-fun-with-sum-and-...

      But perhaps it wouldn't save much code compared to custom-made parsers.

  • vim_wannabe 8 years ago

    In Rust you can also just make your struct deserializable, use a JSON lib to deserialize a string and get the corresponding object in a Result type.

    The amount of work you need to do in Elm instead is huge.

  • askAwayMan 8 years ago

    Purescript's Argonaut lib has major performance issues that are incurred for the "powerful type system."

    When you only have thousands of items in an array, you end up with performance discrepancies between older browsers like IE 11 and Chrome that are unacceptable outside of a toy application.

    • KirinDave 8 years ago

      > Purescript's Argonaut lib has major performance issues that are incurred for the "powerful type system."

      Can you explain what you mean here? Your explanation below suggests a runtime cost for the type system, which just isn't true.

      I'm aware Argonaut's approach generates slower code. The nice part of any such happenstance in PureScript is that if you do find an issue it's easy to bang out some Javascript in a foreign call to do what you need.

      I find outside of Slamdata, folks are keen to do this. PureScript is a very new language and is still finding ways to generate code efficiently in it's target environment.

    • Ar-Curunir 8 years ago

      On the other hand, something like Rust has similar typeclasses, and runs blazingly fast. Serde JSON is super quick and efficient.

antouank 8 years ago

After doing a couple of contracts on Elm projects for several months, and returning now back to a React-Redux stack project, I cannot emphasize enough how much better working with Elm is.

In every single aspect. I just wish that it will get mainstream as soon as possible.

His article is spot on, and agrees with what I've seen, and most others that used Elm. Just look it up.

  • StreamBright 8 years ago

    I am more interested in the details that just this blank statement that everything is better. After trying Elm several times we failed to implement even basic things in it that we could do in React/Redux easily. For an average JS developer Elm is totally alien tech compare to React or Angular. For Haskell programmers Elm is pretty appealing because of the familiar syntax and type system, but the intersection of Haskell programmers and frontend developers are pretty small.

    • antouank 8 years ago

           For an average JS developer Elm is totally alien tech compare to React or Angular
      
      To turn your argument the other way. The JS landscape, where "trendy" libraries change every few months, is also alien to anyone that doesn't keep with the latest libraries every few months. Is that not worse? I don't have to explain "JS fatigue", it's a fact.

      For example, I just got into a new team, and I have to now use what they use. I've been doing JS for 4+ years, and that stack I'm facing now, is alien and confusing to me. I have to invest time and learn what the hell those redux-sagas are, or what "magic" create-react-app tools hide, and in general, it's very confusing so far.

      With Elm, I spent a few weeks 1 year ago to learn the language, and that was it. All the libraries are very simple to use because of the language. I doubt that learning "redux-sagas" will help me a year from now.

      Also, Elm, gives a very coherent package. You don't have to transpile or add a linter, or a type checker, or stitch new libraries every few months because the trend changed. No webpack or babel or eslint or immutable.js or typescript or flow or any of those.

      Not to go into the part of the safety and confidence the compiler gives you. Typescript or flow are not even close to that quality and guarantee level. Note that again you have to learn their syntax and configure them, so that's again a cost on top of learning JS. And they are as much alien as Elm.

      FWIW, I start now side-projects in Elm, and I get things done way faster than with JS. And that's also what I see from other people as well, regardless of whether they come from JS or another language.

      • computerex 8 years ago

        Nothing is forcing you to use the trendiest JS libraries. Feel free to do your side projects in vanilla JS or with whatever library you want.

        The constantly churning and evolving JS ecosystem isn't necessarily a bad thing, although it does demand some attention. On the flip side, there is a lot of pre-written reusable code and more gets added daily.

        From an ecosystem point of view, elm doesn't hold a candle to JS. Also, you don't have to learn every single JS library out there. You don't have to use redux-sagas. Use whatever you want.

        Yes, I know there are a lot of tools and a lot of options and that can be exhausting. But I don't think that's necessarily a bad thing.

        • antouank 8 years ago

          I don't want to, but the "market" is forcing me.

          Being a contractor, I change teams and projects all the time. And most of the time when I join a team with an existing project, the devs that started it are pretty opinionated on everything.

          So sadly simple vanilla JS is not an option if you want to be a preferable candidate for projects. Most of the teams prefer piling libraries upon libraries cause they think it will make things "convenient" or because it's the new exciting trendy library. Whereas I've seen the opposite in the long-term, they make things more complicated and eventually impossible to maintain/reason with.

          I'm not trying to sell Elm to anyone, if you don't like it, don't use it. But to me the advantages are quite obvious and I'd take it over any JS framework/library out there. I just hope I could use it at work as well. There are only a few Elm jobs out there right now.

      • tarr11 8 years ago

        Agree in general that Elm is nicer than Redux. However...

        >>> You don't have to transpile or add a linter, or a type checker, or stitch new libraries every few months because the trend changed.

        Elm is still in alpha, and is adding and removing breaking features all the time. (See ports for example) [1]

        >>> No webpack or babel or eslint or immutable.js or typescript or flow or any of those.

        If you are integrating elm into any kind of existing application, you will likely need webpack. [2]

        >>> I doubt that learning "redux-sagas" will help me a year from now.

        Given redux is an order of magnitude more popular than Elm is, and Redux Saga is the most popular effects handler library for Redux, I would not agree with this statement. Learning about generators is valuable regardless.

        [1] https://github.com/elm-lang/elm-platform/blob/master/upgrade...

        [2] https://www.elm-tutorial.org/en/04-starting/03-webpack-1.htm...

        • rtfeldman 8 years ago

          > Elm is still in alpha, and is adding and removing breaking features all the time.

          Elm has had 2 releases since 2015 which had breaking changes. The elm-upgrade[0] tool has automated away a lot of the upgrade progress, and the compiler tells you about all the remaining things that need to change.

          Put another way, Elm doesn't actually change very often, and when it does, it is typically a smooth ride. (I've been around for 5 breaking releases, and all but one of them were very easy.)

          > If you are integrating elm into any kind of existing application, you will likely need webpack.

          If that existing application is already using webpack, then sure, you'll probably keep it. But you don't need it for Elm.

          For example, we use Elm at work (on a 150,000 LoC Elm code base) and we actually moved away from Webpack because Elm doesn't need it and we weren't happy with its UX. :)

          [0] https://www.npmjs.com/package/elm-upgrade

      • StreamBright 8 years ago

        Well sure, but as an architect/CTO I do not care about trendy, I care only about properly working, easy to develop and maintain and wildly used so I can hire for. Elm is none of these at the moment, but React is.

        • rtfeldman 8 years ago

          Properly working, check.

          Easy to develop and maintain, big check.

          Widely used so you can hire - it's usually easier to hire Elm developers because there are more people who want to use it than jobs hiring for it. The opposite is true for JS. So...better than check, at least for right now! :)

          Here is a CTO giving a talk about his company's experiences with Elm:

          "Elm from a business perspective" - https://www.youtube.com/watch?v=DvQI1KntMhk

        • hellofunk 8 years ago

          The most common stance on hiring Elm developers is to not look for people with experience with Elm (because there are relatively few) but people who are smart and willing to learn it. It is a different way than for hiring in other languages.

          For example:

          https://groups.google.com/forum/#!msg/elm-discuss/92dXqmB4nJ...

          But this is common in niche languages. Most of my background is in Clojure, which has a vastly larger userbase than Elm, but it can still be very difficult to find good developers for it. Many of the professional Clojure developers I know learned the language on the job when they went to work for a company that used it. I wouldn't doubt it is similar with Elm. I saw a presentation once by a CTO of a company that started using Elm in production, and he mentioned that hiring is one of the larger risks to adopting it. But that shouldn't necessarily stop anyone, depends on a company's goals (and spirit).

        • Albert_Camus 8 years ago

          > I care only about properly working, easy to develop and maintain and wildly used so I can hire for. Elm is none of these at the moment, but React is.

          As a CTO and the author of the OP, I can say with confidence this statement is false. Elm works correctly, is easy to develop in, and is easy to train developers that have experience in other languages to write Elm.

    • enalicho 8 years ago

      If you get stuck on anything, come tell us about it on Slack. We can help you get unstuck! The majority of Elm users come from JS -- so while the syntax is different, it should not to be too hard to pick up. This is one of the reasons that Elm has avoided complex features from Haskell. It's really not very much like Haskell beyond syntax. Redux is very much like Elm, seeing as it is an implementation of the Elm Architecture.

  • BoiledCabbage 8 years ago

    > I cannot emphasize enough how much better working with Elm is.

    >In every single aspect. I just wish that it will get mainstream as soon as possible.

    I completely agree - I will be a huge benefit to the industry when elm is as common as js. The concepts it introduces, the benefits it shows are all huge.

    I've been programming a long time, and elm shattered my views of the relationship between a coder and his/her compiler. It went from being that thing you have to "pass" to see your code work, to instead a faithful companion. Like having a trusted dog with you in the woods - an extra set of senses to help you out on your way.

    Elm is not perfect (no language is) -but being perfect isn't the goal. A practical transition story, huge productivity gains and extremely maintainable code.

    • jnbiche 8 years ago

      > I've been programming a long time, and elm shattered my views of the relationship between a coder and his/her compiler.

      I love Elm as much as the next guy, but be aware that you can get many, if not most, of these same benefits while working in JS if you use Flow.js or Typescript. I prefer Flow because the community is more oriented toward functional programming vs OOP in Typescript, but they're both capable static analyzers for JS.

      • tomtheelder 8 years ago

        I would disagree strongly with this, but I don't quite know how to put my objection into words. The enforced functional purity, "true" and incredibly friendly compiler, and the robust message system are all things that I don't think you really get in those other languages. To me, it's a bit like saying that you get all the benefits of Haskell in Java, because Java is also statically typed.

        • jnbiche 8 years ago

          Please read my comment again, I said "many if not most" of the benefits. And I was referring specifically to the tire system, not the language as a whole.

          Listen, I love pure functional languages, but the step up from JavaScript to flow in program correctness is greater than the step up from flow to elm (in my opinion). Flow even has sum types.

          Have you actually tried flow?

          • simplify 8 years ago

            I've tried to use flow three times now, but the tooling just isn't there yet. It might be better now, but Facebook doesn't seem to invest heavily in developer UX.

            • jnbiche 8 years ago

              What problem did you have?? I've been using Flow.js for work for several years now, to great success. I feel very uncomfortable now writing plain JS.

              • simplify 8 years ago

                For some reason I could never get flow's in-editor errors and type information to work. Personally I use TypeScript. Their sublime support is nice and fast.

      • fbonetti 8 years ago

        Cons of using Typescript:

        * Lack of purity. You can unintentionally trigger side effects from a function that's a supposed to simply return a value.

            function add(x: number, y: number): number {
              fireTheMissiles()
              return x + y
            }
        
        * It's impossible to validate that external JSON data is the correct type at runtime. If a JSON API returns something weird, you're going to get a runtime error.
        • jnbiche 8 years ago

          > Lack of purity.

          I agree. Which is why I said "many if not most" benefits, not all benefits.

          > It's impossible to validate that external JSON data is the correct type at runtime.

          Not sure I understand what you're saying. Of course you'll get a runtime error if your external JSON is the wrong type, what else could you do? Accept the data and enter an inconsistent state?

          However, similar to the Elm JSON validation, in Typescript and Flow, you can write schemas to to runtime validation of external JSON. There are multiple libraries for this.

      • Skinney 8 years ago

        I've used both Typescript and Flow, and this breaks down in practice. At least, it did for me.

        Problem with both Typescript and Flow is that it requires discipline from you, your team, and those who provide third party libraries. If someone was lazy and inserted `any` at a key location, you can start having familiar bugs even if the type checker tells you everything is fine.

        • jnbiche 8 years ago

          This is trivial to avoid with a linter (which is a good idea to use anyway with JavaScript). For Flow, there's an eslint plugin that allows you to raise an error on the use of any weak type, such as `any` [1]. I'd be very surprised if the same doesn't exist for TypeScript.

          1. https://github.com/gajus/eslint-plugin-flowtype#eslint-plugi...

          • Skinney 8 years ago

            Cool! Does it enforce third-party libraries as well? A thing that typescript and/or flow doesn't support, is JSON. What if a JSON object doesn't match the type? This can cause sneaky bugs later :/

            I'm not saying that typescript/flow is bad. Whenever I have to use node.js, I end up using typescript. But I've been bitten several times by the fact that typescript isn't as strict as Elm forces things to be.

            • jnbiche 8 years ago

              > Cool! Does it enforce third-party libraries as well?

              Yes, as long as you lint those libraries, too.

              > A thing that typescript and/or flow doesn't support, is JSON. What if a JSON object doesn't match the type?

              That's why you do runtime validations of your incoming JSON if you feel like you can't trust the data (and if it's an external API, you probably can't). How is this different from Elm?

              > But I've been bitten several times by the fact that typescript isn't as strict as Elm forces things to be

              In my experience with Flow, between the Flow analyzer and accompanying linter, you can make it just about as strict as you want with respect to types. The main thing you'll miss (as discussed elsewhere) is that unlike Elm, there's no way to guarantee referential transparency with JS and Flow.

              • Skinney 8 years ago

                It's not different from Elm. My point was that Elm forces you to do it, there's no way around. With Javascript+Flow or TypeScript, you need discipline to end up with code that is as reliable as Elm.

                Me, personally, I take short-cuts when I'm pressed for time, stressed, or in a bad mood (which happens more often than I'd like to admit). I can always improve the code later (which turns into never). In Elm, I can't cut corners (as easily). This makes me write better code using Elm, which in turn makes me happier.

                You --can-- get the same experience with Javascript, but it does require more from you, the developer.

      • fulafel 8 years ago

        That's like saying an angry bear has many of the benefits of a bicycle. It may be factually true but the downsides of riding an angry generally outweigh the benefits, and anyway it's a different kind of thing altogether from a bicycle.

        • laughinghan 8 years ago

          It's nothing like that. The downsides of riding an angry bear relative to a bicycle also apply relative to just walking, to the point that it's always better to just walk than to ride an angry bear for transportation. The downsides of Flow.js or TypeScript relative to Elm don't at all apply relative to vanilla JS, it's often better to use them rather than vanilla JS.

      • gcanti 8 years ago

        working on a fp library for TypeScript if you want to take a look https://github.com/gcanti/fp-ts

    • antouank 8 years ago

      Not perfect, but definitely a much better tool for that work (big front-end apps). JS was not meant for that, it's been stretched far from what it was (quickly) designed for.

      And sure, when more people join Elm, bad-written libraries or silly paradigms will crop up, but I'm confident it won't be as bad as the JS landscape is today.

      Time will tell I guess. -- Now back to stitching those JS libraries to write some code that might work. Bills have to be paid :)

dmjio 8 years ago

If decoding json in Elm is considered hard, I'd recommend checking out miso (https://github.com/dmjio/miso), a Haskell re-implementation of the Elm arch. It has access to mature json libraries like aeson for that sort of thing, along with mature lens libraries for updating your model. Here's an example of decoding json with GHC.Generics using typeclasses. https://github.com/dmjio/miso/blob/master/examples/xhr/Main....

  • enalicho 8 years ago

    You don't need to switch a whole language because of JSON decoding. There are many tools that exist to aid you write JSON decoders in Elm. The language is not just about the architecture -- you can implement the architecture in any language, as Redux has proven. What people like about Elm is the compiler and design philosophy that radiates through the entire community. Switching to Haskell won't give you that, as the Haskell community has different priorities.

    Here are some JSON tools for Elm:

    - json-to-elm http://json2elm.com

    - swagger-elm https://github.com/ahultgren/swagger-elm/

    - elm-graphql https://github.com/jahewson/elm-graphql, https://github.com/jamesmacaulay/elm-graphql

    - elm-export https://hackage.haskell.org/package/elm-export

    • desireco42 8 years ago

      Oh, this is awesome, json2elm really helps :) thanks, didn't know about it.

      • enalicho 8 years ago

        No problem! Glad you found it useful :) It's not perfect by any means, but it's not meant to be.

        • desireco42 8 years ago

          Yeah right, just to jumpstart the effort, especially when there are a lot of entities.

  • a-saleh 8 years ago

    I am kinda waiting for Purescript to mature a tiny bit more in this regard, because it seems that they have something special brewing there, with their polymorphic record type and interesting take on type-level programming.

    Because this [1], even though it seems to be just a experiment so far, looks really good.

    I.e: doing

    type MyTestStrMap = { a :: Int , b :: StrMap Int }

    and then just calling

    let result = handleJSON """ { "a": 1, "b": {"asdf": 1, "c": 2} } """

    let newResult = doSomething (result:: Either MultipleErrors MyTestStrMap))

    is kinda all I ever wanted in these haskell inspired languages?

    [1] https://github.com/justinwoo/purescript-simple-json/blob/mas...

  • aaron-lebo 8 years ago

    This is very cool. The example apps are relatively slow to load compared to the equivalent in some other frameworks (my favorite is Mithril for an idea of how small/fast these can get).

    Was wondering whether it might be a slow server, but the app.js for the TodoMVC appears to be over a megabyte (1.21 MB, have a 4000 line Mithril app which is 500k uncompressed). What's up with that?

    • StreamBright 8 years ago

      Can you give exact numbers and a testing method of how you tested and determined it is slow?

      • aaron-lebo 8 years ago

        The router example on Github loads ~1.5 MB of JS files. Mithril's entire framework is 8kb gzipped (including a router).

        • mbrock 8 years ago

          ghcjs is a pretty heavyweight environment. It's actually translating the compiled core IR from GHC into JavaScript, and including a port of GHC's runtime system.

          It can run basically any Haskell library. That includes the ability to run multithreaded Haskell code -- the JS RTS includes a scheduler. You also get Software Transactional Memory. Lazy evaluation works just as usual -- and so on.

          The tradeoffs become worth it when you have a sufficiently valuable base of Haskell code that you want to run in the browser, and when your users aren't very constrained by page load time. Say, if you have some complicated tricky logic that you don't want to rewrite in another language, but you want to use it in your web app.

  • wereHamster 8 years ago

    Aeson is SLOOW compared to 'JSON.parse' + GHCJS ffi. You lose a bit of correctness (JSON.parse can decode numbers only to the JS precision, ie. 53bits, while Aeson supports arbitrary precision through Scientific), and ability to decode streams in constant memory. But these are features which you most likely don't need in a web app.

  • runeks 8 years ago

    >> Here's an example of decoding json with GHC.Generics using typeclasses.

    >> https://github.com/dmjio/miso/blob/master/examples/xhr/Main....

    There’s no reason to use genericParseJSON here, you can just write “instance FromJSON APIInfo”. Or leave the instance declaration as it is and safely rename your record fields to e.g. currentUserUrl instead of current_user_url.

    The purpose of the camelTo function is to convert “camelCaseExample” into e.g. “camel_case_example” by passing ‘_’ as the first argument. But the APIInfo data structure already uses record fields with underscores in them.

  • Nelkins 8 years ago

    There's also Fable-Elmish[1], which can be used with Fable[2], which is an F#-to-JS compiler.

    [1] https://fable-elmish.github.io/elmish/

    [2] http://fable.io/

  • tiziano88 8 years ago

    To add to the other comments:

    https://github.com/tiziano88/elm-protobuf

  • StreamBright 8 years ago

    This looks really promising. Do you use it in production anywhere? Do you have any comparison of features with Elm?

  • sametmax 8 years ago

    Decoding json, the one most important format of the web, is hard in elm, a language supposedly designed for the web? I'm glad I didn't give it a try.

endgame 8 years ago

I'm really looking forward to the day when all these new-to-Elm people start hitting the complexity ceiling of their language and convince the maintainers to add just a little more power. Example: Haskell's typeclasses have a high power-to-weight ratio, and Elm has to work around their absence (e.g., writing a fresh map function for each data type). Once there's a critical mass of frontend types who understand the power of FP, convincing people to try FP won't be the difficult step any more, and Elm won't need to try so hard to be un-intimidating.

  • chriseidhof 8 years ago

    Every added feature also adds complexity (and to be fair, in some cases, removes complexity). I love type classes, but I discovered in playing with Elm and specifically, TEA (The Elm Architecture) that the simplicity is also really nice even if you're comfortable doing type-level programming. There's something about Elm's simplicity that really works.

    • endgame 8 years ago

      Agreed, but Elm is hurting for some way to deal with this stuff. Currently, the API reference lies by claiming things like (==) : a -> a -> Bool, because it lacks a generic mechanism to let the programmer think the necessary thoughts. (The story for comparison functions like (>) is similar.)

      Now maybe Elm should stay simple and not jump all the way to multi-parameter type classes with functional dependencies, but it is already forced to pay some of the complexity burden to decide which types admit equality testing, comparison and so on.

      • ryanplant-au 8 years ago

        Can you elaborate on what you mean when you say that (==) : a -> a -> Bool is a lie? I don't have enough experience with more powerful type systems to know what the more accurate claim would be. Do you mean that it should really be Eq a => a -> a -> Bool, because not all types can be checked for equality?

        • endgame 8 years ago

          That's exactly what I mean: A type like a -> a -> Bool says that it will work for _any_ a, but we can easily find counter examples. We could pass in functions (say) and write something like (\x -> x * 2) == (\x -> x + 1). The type signature for (==) as written says it _should_ work, but it doesn't because there's no general way to compute function equality.

          Instead, there's this temporary hack[0]:

          > Note: Equality (in the Elm sense) is not possible for certain types. For example, the functions (\n -> n + 1) and (\n -> 1 + n) are “the same” but detecting this in general is undecidable. In a future release, the compiler will detect when (==) is used with problematic types and provide a helpful error message. This will require quite serious infrastructure work that makes sense to batch with another big project, so the stopgap is to crash as quickly as possible. Problematic types include functions and JavaScript values like Json.Encode.Value which could contain functions if passed through a port.

          As you say, Haskell's version of (==) has the type Eq a => a -> a -> Bool, and function types do not belong to Eq.

          [0]: http://package.elm-lang.org/packages/elm-lang/core/5.1.1/Bas...

          • willtim 8 years ago

            It we would be easy to add simple built-in constraints for things like equality, without jumping all the way to full-blown type classes. This would be a very minimal change to the type checker and should not require a rewrite.

            • runeks 8 years ago

              This example with the absence of an Eq class is not related to just equality, it’s a symptom of a lack of generics. You’ll end up with a lot of special, built-in classes (e.g. Ord), while just adding simple type classes to Elm would make all of this solvable in a library (e.g. Elm’s Prelude).

              Surely, if we agree that Eq, Ord, and Foldable are useful, they’re probably not the only useful type classes in existence.

              • willtim 8 years ago

                I think the Elm folks are playing a waiting game. There are alternatives to type-classes such as the proposed modular implicits for OCaml.

            • endgame 8 years ago

              I'm not thrilled by languages the do "rules for thee, not for me". (Another example: old Java had a two-valued enum type (bool) but you weren't allowed to make your own.)

              • willtim 8 years ago

                Agreed in general, but equality is so fundamental that I could forgive any built-in support. Even Haskell has "deriving Eq" built-in for example.

                • tel 8 years ago

                  That said the deriving mechanisms have been mostly exposed to the end used now in at least a few different ways.

                  Even more importantly, deriving doesn't change the semantics of the language. You could in principle do exactly the same things without deriving as you can with.

  • Skinney 8 years ago

    I used several FP languages before coming to Elm, like F# and ClojureScript. I prefer Elm, been using it for about two years, and haven't missed something like interfaces at all.

    Then again, I'm also one of those wierdos who think Go works just fine despite the lack of generics (though I think Go needs generics more than Elm needs something interface'y). Still, things are getting done and I'm happier than with most other languages.

  • quchen 8 years ago

    > writing a fresh map function for each data type

    You have to do that in Haskell as well (where »you« can be you or the compiler via DeriveFunctor); the key difference is at the use site: there is one map function (called fmap) that does the mapping, whereas in Elm (and pretty much all other languages that don’t have higher kinds while we’re at it) you have to use the specific map function for that type.

  • dmitriid 8 years ago

    Can't for the life of me figure out why people equate FP with the abomination that Haskell is.

    Erlang is FP. Javascript is FP. Ocaml is FP.

    Type classes, or anything else that has "types" in them such as dependent, or liquid, are not FP, they are types. Types that found their way into a couple of FP languages.

    • ykler 8 years ago

      I don't think many people (and not the parent) "equate" FP with Haskell, but Haskell is in a way the most functional mainstream language because it is the only lazy one. You can have a purely functional language without laziness in principle, but, as Simon Peyton-Jones points has said, laziness, despite having serious costs, keeps a language designer honest by making it impossible to add side effects. The non-lazy languages all have side effects. A language like OCaml can still be relatively functional because of the way it is typically used. While JavaScript is not traditionally used in a functional way but can be.

      http://www.cs.nott.ac.uk/~gmh/appsem-slides/peytonjones.ppt

      • runeks 8 years ago

        You’re mixing up laziness and purity. Purity makes side effects impossible, so to speak, not laziness.

        Haskell is the most functional language because a Haskell function is a mathematical function, which can only transform its arguments into a value. Everything is a constant in Haskell, and functions transform one or more constants into a single, new constant.

        • tome 8 years ago

          No he/she isn't. The argument (as advanced by SPJ) is that it is so awkward for a programmer to use side-effects in a lazy language that it is (practically, not theoretically) impossible for a language designer to add them.

      • Kiro 8 years ago

        I don't know Haskell but how is laziness related to FP?

        • tel 8 years ago

          The only way I can think to relate them is (a) FP tends to highlight the importance of value-semantics over all others, (b) non-termination is an effect, (c) FP also, subsequent to a, tends to emphasize control of side effects, (d) in a terminating lambda calculus all evaluation strategies are confluent/equal under the value-semantics, thus (e) laziness is particularly _available_ in a FP language.

          • spion 8 years ago

            Attempt at translation from CS-speak:

            Laziness matters less in a FP language because if we consider non-termination an effect (impure), all pure functions should behave exactly the same regardless of whether they're "lazy" or not (plus in FP sameness is defined as same values, due to "value-semantics" - unlike OO where every object has a unique identity, different from all others) because in the absence of side-effects order of evaluation is not important.

            Of course reality is different, and laziness has very visible and important effects when Haskell programs run on today's computers :)

      • dmitriid 8 years ago

        > Haskell is in a way the most functional mainstream language

        1. Is it the most functional language because it's lazy? No

        2. Is it the most mainstream language because it's lazy? No

        3. Is it the most functional mainstream language because it's lazy? No + no = no

        Laziness does not a functional language make.

        > You can have a purely functional language

        What would be the purpose of a pure FP? Oh. There would be no purpose.

        > laziness ... keeps a language designer honest by making it impossible to add side effects

        wat

        Laziness is delayed execution. That's it. There's _nothing_ stopping you from delaying a side effect.

        • chriswarbo 8 years ago

          > Laziness is delayed execution. That's it. There's _nothing_ stopping you from delaying a side effect.

          Laziness is about more than just side effects.

          I think "evaluation" or "reduction" would be better words than "execution" here. Laziness (call by need) is an evaluation strategy for (beta-)reducing expressions, which has two nice properties:

          - If an expression can be reduced without diverging by some evaluation strategy, then it can be reduced without diverging using call by need.

          - Efficiency, in the sense that no duplicated work is performed.

          The other common evaluation strategies are call by name and call by value. Call by name has the first property, but not the second; so there are cases when it's exponentially slower than call by need. Call by value has the second property, but not the first, so there are cases when it diverges unnecessarily.

          This 'unnecessary divergence' is a major reason why most programming languages end up overly complicated to understand (at least, mathematically). For example, consider something like a pair `(cons x y)`, and its projection functions `car` and `cdr`. We might want to describe their behaviour like this:

              ∀x. ∀y. (car (cons x y)) = x
              ∀x. ∀y. (cdr (cons x y)) = y
          
          This is perfectly correct if we're using call by name or call by need, but it's wrong if we're using call by value. Why? Because under call by value `(car (cons x y))` and `(cdr (cons x y))` will diverge if either `x` or `y` diverges. Since the right-hand-sides only contain one variable each, they don't care whether or not the other diverges.

          This is why Haskell programs can focus on constructing and destructing data, whilst most other languages must concern themselves with control flow at every point (branching, looping, divergence, delaying, forcing, etc.).

        • foldr 8 years ago

          >Laziness is delayed execution. That's it. There's _nothing_ stopping you from delaying a side effect.

          This is true, but unconstraimed side effects are too difficult to reason about in a lazy language to make them practical. So in practice, very few lazy languages have unconstrained side effects.

    • willtim 8 years ago

      What definition of FP are you using? Because if it's just first-class functions, even Visual Basic has this now.

      • dmitriid 8 years ago

        Exactly.

        Let's take Wikipedia:

        -- start quote --

        In computer science, functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. It is a declarative programming paradigm, which means programming is done with expressions[1] or declarations[2] instead of statements. In functional code, the output value of a function depends only on the arguments that are passed to the function, so calling a function f twice with the same value for an argument x will produce the same result f(x) each time

        -- end quote --

        • willtim 8 years ago

          Only Haskell strictly conforms to that definition. None of your other examples do.

          • dmitriid 8 years ago

            No. It only means that other languages support multiple paradigms.

            Haskell doesn't strictly conform either, because it allows side effects (event though it's "only through escape hatches"). There's no such thing as a "pure functional programming language".

            Let's take it from the top:

            "In computer science, functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data."

            All of the languages I listed support this style. The moment you write an IO monad inside a function in Haskell, you break the illusion of Haskell's strict conformation to the definition.

            • willtim 8 years ago

              Now you have the condescending tone! However, it appears you don't understand the IO monad. Programming using the IO monad is pure functional programming with full referential transparency. Please read Phil Wadler's paper, you will not understand this from that JavaScript snippet. The only backdoors are escape hatches like unsafePerformIO which are used for low-level libraries and FFI, they can be disabled with pragmas and/or compiler switches.

              It is much harder to stay true to that definition using the other languages, multi-paradigm or not. That is why Haskell is so often mentioned in the context of FP.

              • dmitriid 8 years ago

                As soon as you have side-effects (an IO is a side-effect) you can through your assumptions about "strict conformation to definition" out of the window:

                --- treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. ---

                > It is much harder to stay true to that definition using the other languages, multi-paradigm or not.

                I doesn't matter if it's "hard". Your argument was that "Only Haskell strictly conforms to that definition."

                No. Haskell conforms stricter. It doesn't mean that none of the other languages are not FP, or cannot be used to program in functional style.

                > That is why Haskell is so often mentioned in the context of FP.

                Yes. And the problem is that people treat the features that Haskell has as requirements to be considered a functional programming language.

                - Typeclasses/Liquid types/Dependent types are not FP

                - Pattern matching is not FP

                - Laziness is not FP

                - A whole bunch of anything else is not FP

                • willtim 8 years ago

                  > As soon as you have side-effects (an IO is a side-effect) you can through your assumptions about "strict conformation to definition" out of the window

                  Again, you are unfortunately quite wrong. You do not understand the IO Monad. No IO is ever performed in any code written inside the IO monad (unless using unsafePerformIO).

                  Please take some time to fully understand Haskell before criticising it so openly on a public forum. Perhaps then it might not all seem rather pointless.

                  • dmitriid 8 years ago

                    So, you're telling me that Haskell never does any output and never reads any input.

                    In this case (an only in this case) would it strictly conform to FP definition.

                    • willtim 8 years ago

                      Haskell is used to wire-up the IO actions, they are then delivered to the runtime via the main definition. The runtime performs the IO actions.

                    • spion 8 years ago

                      Indeed Haskell the language doesn't do any IO. Instead, it creates a tuple containing

                      * a description of the action to execute (e.g. read line from STDIN)

                      * a function that will take the result of the previous action (e.g. the line) and return the next action to execute (e.g. writeLine)

                        [readLine, \line -> writeLine(line)]
                      
                      This main action is just a description, the runtime itself takes it, does the actual IO described in the first part of this tuple, evaluates the second (function) part of the tuple with the result of that IO, receives a new pair of action/function, does the action's IO part, evaluates the new function and so on until it gets a nil (end of the "linked list" so to speak)

                      You could imagine this as a linked list of actions, where the "link" is actually a pure function you call with the result of execution the first part to get the rest of the list (or nil to terminate). This is still pure because the action itself doesn't do anything. If you return an action from a function, it doesn't actually execute, its just a value to be interpreted.

                      Does that make a real difference or is it just theoretical "purity"? Yes it does make a difference.

                      For example, if you create two action values, you haven't actually run anything yet, just created descriptions of actions. You could put them in a list and create an action description that will tell the runtime to run them in parallel, for example using: https://hackage.haskell.org/package/parallel-io-0.3.3/docs/C... ... or in sequence https://hackage.haskell.org/package/base-4.10.0.0/docs/Prelu...

                      For refactoring, it means that you can still take functions that return actions and substitute them with their values, e.g. if you have

                        readItems 0 = return []
                        readItems n = do
                          x <- readLine
                          y <- readItems (n - 1)
                          x :: y
                      
                        main :: IO
                          items <- readItems 3
                          putStrLn "The first item is " ++ (head items)
                      
                      
                      you could pull (readItems 3) out of main!

                        read3Items = readItems 3
                        
                        main :: IO
                          items <- read3Items
                          putStrLn ...
                      
                      and everything is exactly the same, since all you've pulled out is a description of an action. Equational reasoning (you can substitute an equation with its result) still works - which is great for refactoring!
                  • dmitriid 8 years ago

                    Basically, the moment you have

                      readFile  :: FilePath -> IO String
                      writeFile :: FilePath -> String -> IO ()
                    
                    and any function invoking those two (and any functions invoking these function etc. etc.) your "haskell strictly conforms to FP definition" flies out of the window.

                    And yes: I used the term "IO monad" incorrectly.

                    • willtim 8 years ago

                      These functions do not perform IO, they return IO actions that can be further composed. They are invoked from other functions that also return IO actions, again no IO is actually performed until the top-level final composite action is run by the runtime.

                      All composition of IO actions is performed with full referential transparency and adherence to the Wikipedia definition.

                      • dmitriid 8 years ago

                        Potato potato. In the end, when the program is run, functions will be run, and side-effects will be executed.

                        Moreover, if you invoke these functions multiple times they will produce different results.

                        Hence, no strict adherence to FP principles.

                        • willtim 8 years ago

                          > Moreover, if you invoke these functions multiple times they will produce different results

                          No this isn't true! Again, you clearly do not yet understand Monads. Please read Phil Wadler's paper.

                          What you have successfully demonstrated, is that the JavaScript snippets you have been advocating are not sufficient to understand Monads.

                        • empath75 8 years ago

                          > if you invoke these functions multiple times they will produce different results.

                          the io monad is a pure function that produces impure code for the haskell runtime to execute (this isn't exactly accurate, but i think is an okay way to think about it). It will always produce the same impure code, and so is itself pure.

                          • dmitriid 8 years ago

                            Riight. The "Platonic ideal" again. "It's not the language it's the runtime!"

                • seagreen 8 years ago

                  You're making some good points here. Neither category theory informed typeclasses nor laziness are required for pure functional programming (henceforth PFP). Up until recently Haskell was the only PFP language in common use, so all those things got conflated in a lot of people's minds. Now we have PureScript (which doesn't have laziness) and Elm (which doesn't have laziness or typeclasses).

                  That said, you really should listen to @willtim about the `IO` type. Purity really is a big deal, and `IO` doesn't break it at all.

                • runeks 8 years ago

                  > As soon as you have side-effects (an IO is a side-effect) you can through your assumptions about "strict conformation to definition" out of the window

                  You misunderstand what an IO action is. An IO action in Haskell is a value which describes a side effect. For example, an IO Int is a value that describes a side effect which, at runtime will produce a value of type Int inside the IO monad.

                  The difference is that the IO Int is a value, it’s a constant which describes how to perform something at runtime (also called promises in some languages). It’s like a callback function, which takes a value as an argument that will be available when it’s called (at runtime).

    • farresito 8 years ago

      What makes you say Haskell is an abomination?

      • dmitriid 8 years ago

        Short version: You need a PhD in type theory to get anywhere.

        Basically every obscure overly complicated concept that Haskell throws at you (all the while pretending to be the only true FP language out there) can be explained in 5 to 10 lines of Javascript: https://github.com/hemanth/functional-programming-jargon

        Compare and contrast.

        - Monad explained in Javascript: https://github.com/hemanth/functional-programming-jargon#mon...

        - Timeline (sic!) of monad tutorials for Haskell: https://wiki.haskell.org/Monad_tutorials_timeline

        The worst crime against humanity though is Haskell crap seeping into other languages (such as ramda, for instance: http://ramdajs.com)

        • mrkgnao 8 years ago

          > You need a PhD in type theory to get anywhere.

          That is extremely false. Haskell isn't even a good playground for academic type theory -- you'd want Agda etc. for that. The development of the language over the last few years has been characterized by pragmatism and a focus on backwards-compatibility, which is why you can take code from something like ten years ago and have it run without issues on modern versions of the Haskell compiler with little to no modifications. (Let's not talk about how long code written in "modern" JS lasts.)

          And I'd really like to see type class constraint resolution with functional dependencies, or Hindley-Milner type checking, or something of that sort implemented in "5-10 lines" of JS.

          "There he goes again with his mumbo-jumbo," you say. That's right, you don't need to care about those things to write Haskell. What you meant is implementations of typeclasses ("interfaces") like Monad, Functor, and so on: they don't take much more code in Haskell.

            Array.prototype.chain = function (f) {
              return this.reduce((acc, it) => acc.concat(f(it)), [])
            }
          
            instance Monad [] where
              xs >>= f = concat (map f xs)
              return = pure
          
          And we didn't even have to go the "this" route! Notice that your 5 - 10 lines of JS don't let you write code that works in any monad, whereas I can easily write

            whenM :: Monad m => m Bool -> m () -> m ()
            whenM cond action = do
              condition <- cond
              if cond then action else pure ()
          
          In Elm, you'd have List.whenM, Array.whenM, Maybe.whenM, ... or a straight-up false type signature like their Eq ones, and in JS, a bunch of prototype methods with no unifying threads.

          --

          As for an example of why I think Haskell has the right ideas (few of us will say it's the "best language evar"):

          I'd really like to see a JS version of the Servant library, which takes an API spec for a server and actually generates a fully functional server from that. Here's a description:

          https://news.ycombinator.com/item?id=14149200

          Does this strike you as idle theoretical self-enjoyment?

          • dmitriid 8 years ago

            > This is extremely false [referring to "You need a PhD in type theory to get anywhere."]

            almost immediately followed by

            > And I'd really like to see type class constraint resolution, or Hindley-Milner type checking

            You don't even see the irony in that, do you?

            > they don't take much more code in Haskell:

            riiight. I won't even go into the number of things that need to be explained there before you even start explaining what the code does.

            • mrkgnao 8 years ago

              I hadn't really finished editing my comment then. (I was eating at the time, haha.) I just saw your reply now.

              • dmitriid 8 years ago

                So, you could say your comment was ... lazy?

                (huehuehue, lame joke, I know :) )

          • dmitriid 8 years ago

            Commenting on the edited comment :)

            > Notice that your 5 - 10 lines of JS don't let you write code that works in any monad, whereas I can easily write

            Maybe, maybe not. Depends on your requirements, really. The core language might never get this, but these 5-10 lines of code do some very important things:

            - they explain monads faster and clearer than any of the countless monad tutorials that exist for Haskell

            - they demystify monads and show that: hey, you've probably been writing monads all along (and re-implemented them yourself countless of times, no doubt)

            - they (by necessity) dumb down the jargon-heavy lingo for easy consumption by average Joes like me :)

            Edit: that page in particular has also shown me that I have used easily half of Haskell's things (functors of all flavors, monads, comonads, etc. etc. etc.) countless times over the years in Javascript and Erlang. I didn't even know I did, because no one scared me off with the theory, and strange explanations and names :)

            • tel 8 years ago

              Is it fair to say that your argument here is that "this resource was extremely valuable to me for understanding certain concepts in ways that Haskell-oriented resources in the past have not been"?

              I think that's a totally fair criticism. I also believe that the Haskell resources can provide further value to you (and others in your position) over time if you choose to study them. Similarly, studying category theory or type theory or logic could.

              Are these practical things to do? It depends upon your goals.

            • mrkgnao 8 years ago

              Sure! I don't disagree: Haskell learning materials are a far cry from adequate, and we definitely need to learn from, e.g. the Rust/Elixir/Elm communities here. For now, this is worth trying:

              http://haskellbook.com/

              Also, #haskell on IRC has been, without a doubt, one of the friendliest learning environments I've ever seen. Drop by sometime if the mood strikes you. :)

            • willtim 8 years ago

              This is not really a definition of a Monad. For example, there's no mention of the Monad laws.

              Monads are a very general and powerful abstraction that are not adequately described by your example. My advice to anyone is to read Phil Wadler's seminal paper, it is very easy to read.

          • dmitriid 8 years ago

            > Does this strike you as idle theoretical self-enjoyment?

            It does.

            How many PhDs does one require to understand/correct/debug all the :> and :<|> etc.?

            • mrkgnao 8 years ago

              Speaking from experience, zero.

              > debug

              But the whole point of a good compiler is that it tells you when you're wrong! (Instead of having to write hundreds of tests (and thousands of Node test runners).)

              > :> and :<|>

              You can just treat them as syntax, like the largest proportion of every other language, but with the opportunity of actually being able to write things like that yourself later.

              Observe:

                  type API = "polls"                                           :> Get  '[JSON] [Poll]
                        :<|> "polls" :> Capture "question_id" Int              :> Get  '[JSON]  Poll
                        :<|> "polls" :> Capture "question_id" Int :> "results" :> Get  '[JSON]  PollResults
              
              
              The :> operator separates parts of a path, and the :<|> separates different URL patterns. This is the equivalent of this API from the Django documentation:

                  urlpatterns = [
                      # ex: /polls/
                      url(r'^$', views.index, name='index'),
                      # ex: /polls/5/
                      url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
                      # ex: /polls/5/results/
                      url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results')
                  ]
              
              The only difference is it has less regexes in it, is capable of being checked for nonsense by a compiler much smarter than me, and gives you the aforementioned "server for free". I have had URL pattern-match errors with Django in the past, and having your compiler check that there aren't any is excellent.

              Easier to maintain? Check.

              Easier to read? Check. (If nothing, because of the lack of regexes.)

              Defines the response type too? Check.

              Easy to refactor? Check! Tired of typing "polls" at the beginning? Just lift it out: turn

                  type API = "polls"                                           :> Get  '[JSON] [Poll]
                        :<|> "polls" :> Capture "question_id" Int              :> Get  '[JSON]  Poll
                        :<|> "polls" :> Capture "question_id" Int :> "results" :> Get  '[JSON]  PollResults
              
              into

                  type API = "polls" :> 
                           (                                            Get  '[JSON] [Poll]
                        :<|>  Capture "question_id" Int              :> Get  '[JSON]  Poll
                        :<|>  Capture "question_id" Int :> "results" :> Get  '[JSON]  PollResults
                           )
              
              Types are first-class :)

              > How many PhDs does one require to understand/correct/debug all the :> and :<|> etc.?

              Definitely less than it takes to become comfortable with the quirks of literally everything in JS: perhaps you should give something an honest shot before telling people who have derived real-world benefits from using it in production that it's useless?

              • dmitriid 8 years ago

                > Speaking from experience, zero. You can just treat them as syntax, like the largest proportion of every other language, but with the opportunity of actually being able to write things like that yourself later.

                So, basically, "learn this thing without understanding what it does" :-\

                Reminds me of teaching Java to newbies: "oh, just type this syntax, you have to memorize it, don't worry about it".

                > Definitely less than it takes to become comfortable with the quirks of literally everything in JS: perhaps you should give something an honest shot before telling people who have derived real-world benefits from using it in production that it's useless?

                A real app is not just "hey, memorize this DSL and type it". I've found Haskell unapproachable on multiple occasions. And yes, I've completed my obligatory "Haskell from first principles" and "Learn You a Haskell for Great Good!" :)

                • mrkgnao 8 years ago

                  Well, Servant isn't introductory Haskell material. My point was to show that the advanced type system features of modern Haskell are useful in the real world, for, e.g. building webapps that real people use and ship to production.

                  If you've picked up the stuff in LYAH, you're ready to learn what type operators are (that's where :> and friends come from, they're just things like Maybe but written infix, so they're probably defined as

                  data a :> b = ColonPointyThing a b

                  or something like that.) Servant then pattern-matches on these types, essentially. For instance, if I can handle an API that serves endpoint A, and one that serves endpoint B, I can handle an API that serves both:

                      instance (Handler A, Handler B) => Handler (A :<|> B) where
                  
                         handle req = ...
                  
                  That's the idea.

                  You'd hardly expect a beginner to pick up, I dunno, using React and Redux on a Webpack hot-reloadable setup on day 1 of "Javascript 101", but React is one of the best ways to sell modern web development (at least when I've been buying).

                  • dmitriid 8 years ago

                    The problem is basically how to arrive at the "same level" in Haskell and in JS.

                    Basically: what does it take to define (or even to just understand) a type-level DSL in Haskell, and a sufficiently advanced library in Javascript.

                    I can take apart almost any JS library and see/understand how it works. How much type-foo do I need to understand Servant? Or any other sufficiently complex Haskell library (a friend of mine has struggled mightily with Yesod and gave up after a month or so).

                    • mrkgnao 8 years ago

                      I'm pretty sure you'll be able to follow the Servant docs, which are quite nice:

                      https://haskell-servant.readthedocs.io/en/stable/

                      There are definite advantages, and if you're willing to put in as much work as one, say, unconsciously puts in while setting up JS frameworks, understanding libraries like this is definitely doable. (Without the dissertation.)

                      • dmitriid 8 years ago

                        The docs are nice, but they complement the code :)

                        Basically to actually understand what's going on, I'll need to learn DataKinds (at the very least).

                        And just to implement something as simple as BasicAuth, this is what I have to start with (https://haskell-servant.readthedocs.io/en/stable/tutorial/Au...):

                          {-# LANGUAGE DataKinds             #-}
                          {-# LANGUAGE DeriveGeneric         #-}
                          {-# LANGUAGE FlexibleContexts      #-}
                          {-# LANGUAGE FlexibleInstances     #-}
                          {-# LANGUAGE MultiParamTypeClasses #-}
                          {-# LANGUAGE OverloadedStrings     #-}
                          {-# LANGUAGE ScopedTypeVariables   #-}
                          {-# LANGUAGE TypeFamilies          #-}
                          {-# LANGUAGE TypeOperators         #-}
                          {-# LANGUAGE UndecidableInstances  #-}
                        
                        You rarely need anything beyond regular JS to understand the inner workings of most libraries.
                        • mrkgnao 8 years ago

                          Servant isn't the "simplest" way to get BasicAuth: you get what you pay for.

                          You can just use one of the raw HTTP servers, like wai or something. They expose an interface not unlike the ones you'll find in, say, Go or C++ or Node or something.

                          Also:

                            {-# LANGUAGE FlexibleContexts      #-}
                            {-# LANGUAGE FlexibleInstances     #-}
                            {-# LANGUAGE MultiParamTypeClasses #-}
                            {-# LANGUAGE TypeOperators         #-}
                            {-# LANGUAGE UndecidableInstances  #-}
                          
                          These extensions only serve to lift a couple of restrictions that the compiler imposes because the original Haskell Report did. DataKinds and TypeFamilies are the real "new ideas" that you need to pick up after a book at the level of LYAH to understand Servant.
                          • dmitriid 8 years ago

                            > Servant isn't the "simplest" way to get BasicAuth: you get what you pay for.

                            But that's not what I complained about ;)

                            It's funny how in a separate thread someone insists that I have to understand the whole concept of a monad, its three laws, read Wadler's paper etc.

                            In his thread however, it boils down to: oh, just memorise these lines of code, and just blindly copy-paste them wherever.

                            So, my complaint is: to actually understand what's going in the "simplest way to do BasicAuth" I need a whole lot more than just look at the code.

                            I need to understand why I need no less than ten (!) language extensions before I even begin to implement BasicAuth. And five of them just to workaround some Haskell limitation based on some report from 1998 (I'm guessing)? What happens when I move on to OAuth? What will I need then?

                            Also, "oh, you don't really need PhDs in type theory to use Servant" slowly descends to "oh, here are two things regarding types that you'll have to learn". On of them has no useable documentation except some comment on StackOverflow. The other one requires you to be well-versed in type theory.

                            Hence my complaint about "you need a PhD in type theory to work with Haskell".

                            • mrkgnao 8 years ago

                              > "simplest way to do BasicAuth"

                              The simplest way to do BasicAuth in Haskell isn't Servant. I feel like you're intentionally misinterpreting what I'm saying.

                              As for resources, here's a great intro to type-level Haskell:

                              http://www.parsonsmatt.org/2017/04/26/basic_type_level_progr...

                              > For some reason, functions that operate on types are called type families.

                              That's the kind of thing you need to know.

                              I doubt the average JS user knows how V8 optimizes execution of code, or whatever. That's what your insistent complaints about type theory amounts to: none of the articles I'm linking to mention inference rules or type judgments or what-have-you. That's what all the research papers about Haskell are for, which you do not need to read to use this language. Even advanced Haskellers don't do type theory (category theory, maybe, not type theory). Only a couple of people working on the compiler do.

                              More DataKinds:

                              https://stackoverflow.com/questions/20558648/what-is-the-dat...

                              http://ponies.io/posts/2014-07-30-typelits.html

                              https://www.schoolofhaskell.com/user/k_bx/playing-with-datak...

                              • dmitriid 8 years ago

                                > The simplest way to do BasicAuth in Haskell isn't Servant. I feel like you're intentionally misinterpreting what I'm saying.

                                True, I re-read what you wrote, and argh. I need to learn to read.

                                > As for resources, here's a great intro to type-level Haskell: > That's the kind of thing you need to know.

                                So, we're basically returning to the root of my complaints

                                > I doubt the average JS user knows how V8 optimizes execution of code

                                Wait. Are you telling me now is that writing a BasicAuth implementation in a "simple library that doesn't require you to have a PhD in type theory" is on the same level of complexity as knowing the inner workings of an advanced Javascript VM?

                                > That's what your insistent complaints about type theory amounts to: none of the articles I'm linking to mention inference rules or type judgments or what-have-you.

                                No, they don't.

                                I'm looking at Servant and its examples. In order to write an extremely simple and basic piece of code, I, as a programmer:

                                - have to pull in no less than 10 language extensions

                                - five of those extensions are just workarounds some obscure Haskell rules (?)

                                - in order to understand just the basics of what's going on in there I need to know why when and where these extensions are used, how they work etc.

                                What happens the moment I step outside the bare necessities of the extremely simple BasicAuth implementation, for instance?

                                > More DataKinds:

                                This exactly what I wrote: as soon as you step outside into a real world of Haskell, oh "here's a list of increasingly obscure things you need to know. Maybe two people on StackOverflow know about them. For the rest, please proceed to your nearest university to obtain a PhD or two".

                                • tel 8 years ago

                                  Your argument seems to be that "Real World Haskell" uses obscure features that I and many others don't understand, thus Haskell is complex. This is true if it is (a) impossible to write "Real World" Haskell without using these features and (b) that these features are truly complex and not just unfamiliar.

                                  An alternative hypothesis to (a) is that Real World problems can be solved by simple Haskell, but more sophisticate Haskell features pay their way often enough that skilled practitioners choose to use them nearly always. I don't know if I completely buy this, but I also don't know that I completely buy that there aren't examples of Real World Haskell that are simple.

                                  Of course (b) is easy to criticize and painful to do so since it'll ultimately be this indefensible argument of "if only you knew what I know then you'd agree with me" which I think is stupid. Unfamiliarity is a complexity since it forces investment on all that would learn it---languages which avoid unfamiliarity are faster and more valuable tools for avoiding forcing that investment.

                                  The only counterargument is a global one: if these techniques _are_ worth the investment then they will over time have an increasingly large impact on the culture of programming at large. Already this is coming true with first class functions, immutable data, preference for stronger typing, option/maybe types. Your personal investment into learning further ideas may be worth it if they pay out over a longer time period either by preparing you for where things are going (speculative) or by diversifying your thought process immediately (less speculative).

                                  So you get people encouraging folks to learn Haskell because they personally have made the judgement that learning these things is great. If you're unconvinced that's a totally reasonable position to take. OTOH, learning new things can be fun and there's at least a small hill of anecdotal evidence that these things can pay their way at times.

                                  • dmitriid 8 years ago

                                    > OTOH, learning new things can be fun and there's at least a small hill of anecdotal evidence that these things can pay their way at times.

                                    Life is finite, number of things to learn is near-infinite.

                                    Do I have the lifetime to learn 10 Haskell language extensions to understand how the most basic piece of code works?

                                    • tel 8 years ago

                                      Why are you asking me?

                                      If it's to imply that the answer is "obviously not" so as to project it to other readers then why are you trying to answer for them?

                                      Clearly others have decided that what you suggest is either unnecessary or worth it. But it's your choice.

                                • mrkgnao 8 years ago

                                  > I doubt the average JS user knows how V8 optimizes execution of code, or whatever. That's what your insistent complaints about type theory amounts to: [...] Only a couple of people working on the compiler [use type theory].

                                  > Wait. Are you telling me now is that writing a BasicAuth implementation in a "simple library that doesn't require you to have a PhD in type theory" is on the same level of complexity as knowing the inner workings of an advanced Javascript VM?

                                  I rest my case. You're not arguing in good faith here, although you seemed to be doing a pretty good job at times. I'm saying that knowing enough to be able to add big new features to the Haskell compiler, like the computer scientists who drive GHC development, is similar to knowing how V8 works. :)

                                  • dmitriid 8 years ago

                                    > I'm saying that knowing enough to be able to add big new features to the Haskell compiler, like the computer scientists who drive GHC development, is similar to knowing how V8 works. :)

                                    Once again: THIS IS NOT WHAT I'M COMPLAINING ABOUT

                                    I wonder if you have enough good faith to even see what I'm talking about.

                • codygman 8 years ago

                  >> Speaking from experience, zero. You can just treat them as syntax, like the largest proportion of every other language, but with the opportunity of actually being able to write things like that yourself later.

                  > So, basically, "learn this thing without understanding what it does" :-\

                  > Reminds me of teaching Java to newbies: "oh, just type this syntax, you have to memorize it, don't worry about it".

                  I don't think what's recommended is the same.

                  • dmitriid 8 years ago

                    Let's consider this quote:

                    --- start quote ---

                    > :> and :<|> You can just treat them as syntax, like the largest proportion of every other language, but with the opportunity of actually being able to write things like that yourself later.

                    --- end quote ---

                    This is exactly what's recommended: just blindly type these things, your understanding is not required.

                    It becomes worse. Link: https://news.ycombinator.com/item?id=14890937

                    You need 10 language extensions to implement the simples things. Answer to that complain?

                    Oh, five of them are just workarounds [so, just blindly copy-paste them]

        • willtim 8 years ago

          You certainly would need a PhD to fully understand Monads from that small JavaScript snippet. The Haskell link you gave gives Phil Wadler's original paper as the first link. It is easy to read, explains everything beautifully and full of many examples. Learn some basic Haskell for no other reason than to read seminal papers such as these. To favour some random JavaScript hacker on the internet and steer others away from the original work is anti-intellectualism.

          • dmitriid 8 years ago

            Ah, here comes the condescending tone I've so come to appreciate from the Haskell programmers.

            "Go and read", "anti-intellectualism".

            • eastWestMath 8 years ago

              Wadler's paper is an excellent piece of exposition that's us at the level of an upper-year undergraduate textbook. There's nothing condescending about referring a professional to a relevant paper in their discipline, but it is troubling when a professional won't even read over a paper.

              • dmitriid 8 years ago

                It's troubling when people assume there's only one paper that a professional should read. Or that a professional cannot choose between papers to read. etc.

                • KirinDave 8 years ago

                  How did anyone imply this? A single free, reputable resource was offered, but many more exist.

            • willtim 8 years ago

              You are misquoting me. I said steering others away from the original source of work to an interior source (incomplete at best) is anti-intellectualism.

              I do not mean to be condescending, but I feel very strongly about this.

            • KirinDave 8 years ago

              Wait... you're tone policing haskell users after referring to even the _adaptation_ of functional techniques as a "crime against humanity?"

              Please rethink this approach. It is a bad approach. It fails to capture (what I think you) your argument (is) and antagonizes people needlessly. And quite frankly, a lot of people are being VERY nice by not following in the tradition of absolutely burying javascript for its nonsensical primitive type semantics.

        • tome 8 years ago

          > Basically every obscure overly complicated concept that Haskell throws at you (all the while pretending to be the only true FP language out there) can be explained in 5 to 10 lines of JavaScript

          So presumably the same concepts can be explained in 5 to 10 lines of Haskell too.

          I think you're confusing the refinement and polishing of ideas that's taken place in Haskell over the last two decades with the succinct presentation of those ideas once they've been worked out.

          • dmitriid 8 years ago

            But they can't, can they. Or we wouldn't have the bazillion monad tutorials.

            The funny thing, this is the trouble that plagues other Haskell-inspired work (such as Purescript)

            • tome 8 years ago

              It's obvious that the concept of monad can either be explained in 5-10 lines in both JavaScript and Haskell, or neither. Which are you claiming?

              • dmitriid 8 years ago

                It's not obvious.

                I'm claiming that:

                - the concept of monad can be explained in 5-10 lines in Javascript (demonstrable)

                - the concept of a monad requires multiple years and tens of tutorials in Haskell (also demonstrable)

                • KirinDave 8 years ago

                  I just want to reiterate: point 1 is totally false. That description you linked is incredibly incorrect, captures almost nothing of the spirit of what a monad is, and is somewhat disingenuous.

                  Lots of people get excited about "monads" and then rush out to write tutorials to try and capture whatever mental model they're using. These mental models may arrive at correct results most of the time, but they're often not really transferrable to another human.

                  Learn You A Haskell takes a different approach, in which you arrive at creating monads because you naturally derive them as a way to deal with the tedium of functional code w/out such mechanisms.

                  "Monad tutorials" are becoming much less frequent now that such approaches are offered. Everyone just says, "Go read this chapter or two of this freely available book and you're good to go."

                  You know, just like any major feature in javascript.

                • mrkgnao 8 years ago

                  Demonstrate a pair of tutorials aimed at the same level of reader, one in JS, and one in Haskell, where you end up with a better understanding of the idea of a monad reading the former (as opposed to the implementation of the Monad instance for a list).

                  • dmitriid 8 years ago

                    TIL there's some "idea of a monad".

                    Basically this is (in my mind) what's wrong with Haskell: it's overly concerned with the Platonic ideal.

                    Meanwhile that one page on jargon has shown me that I effortlessly implement any and all of those things daily (and understanding what I'm doing) without the need to understand "an idea". I just use the tool that solves the problem. If someone insists on calling this "monadic composition", or "lifting over typeclasses", or "zygohistomorphic prepromorphisms", so be it.

                    • mrkgnao 8 years ago

                      It's code reuse for concepts. Wouldn't you agree that reusing intuition about things is good? It's the same as knowing what big-O is instead of just memorizing "bubble sort is slower than insertion sort, insertion sort is sometimes faster than quicksort but usually not", or knowing what concurrency is instead of memorizing the API of a library in your favorite language.

                      • dmitriid 8 years ago

                        The entire "concept" of a monad fits in that description I linked to. It can be easily reused (which I've done numerous times with it, and with other concepts on that page).

                        Haskell for some reasons insists that I should only go for "The concept of a monad, which arises from category theory, has been applied by Moggi to structure the denotational semantics of programming languages" and

                          A monad is a triple (M,unit,⋆) consisting of a type constructor M and two operations of the given 
                          polymorphic types. These operations must satisfy three laws given in Section 3.
                          
                          We will often write expressions in the form
                            m ⋆ λa. n
                        
                        Should I? Really?
                        • mrkgnao 8 years ago

                          Yes, you should care about the laws!

                          They allow you to edit code without the aforementioned released-last-week test runner having to check all your code after a big refactor. I mean, you can't call something with a "flatMap" method and a "return" method a monad! There are tons of nonsensical definitions that fit that which are going to become very unpleasant to use quickly.

                          • dmitriid 8 years ago

                            > They allow you to edit code without the aforementioned released-last-week test runner having to check all your code after a big refactor.

                            Oh wow. How did I ever live with big refactorings before?

                            > There are tons of nonsensical definitions that fit that which are going to become very unpleasant to use quickly.

                            I recently learned that, apparently, I've been using "monads" and "monadic composition" for years now, never knowing what it is. Can't remember any unpleasantness that would "quickly arise".

                            There are other things than blind following after a Platonic ideal.

                            • mrkgnao 8 years ago

                              The unpleasantness arises when writing Monad instances that don't follow the laws. Setting your disingenuity aside, try using

                                xs >>= f = concat (reverse (map f xs))
                              
                              or

                                Array.prototype.chain = function (f) {
                                  return reverse(this.reduce((acc, it) => acc.concat(f(it)), []))
                                }
                              
                              
                              (I don't know how JS works, but you get it) instead of

                                  xs >>= f = concat (map f xs)
                              
                              and enjoy refactoring your code, blissfully and consciously ignorant of the laws that make a monad a monad. The laws aren't just supposed to enrich the "life of the mind".
                              • dmitriid 8 years ago

                                So, what problems will I experience while refactoring this code (if I ever wrote it)?

                                • mrkgnao 8 years ago

                                  It won't satisfy the properties you've come to expect from the implementations you know for lists, optionals (Maybe), and so on.

                                  Suppose you're going over some code, where you have something of the form

                                     xs >>= someFunction
                                  
                                  (where xs is a list) and you change someFunction to "return":

                                     xs >>= return
                                  
                                  Then you'd expect, from experience, that you can rewrite this to just

                                     xs
                                  
                                  (which you can check works with all the monads you know about) but that doesn't hold for this wonky definition of the Monad instance for lists. Indeed, note that return for lists has the definition

                                     return x = [x]
                                  
                                  so, with our bad >>=,

                                     [1, 2, 3] >>= return
                                     = concat (reverse (map (\x -> [x]) [1, 2, 3]))
                                     = concat (reverse ([[1], [2], [3]]))
                                     = concat [[3], [2], [1]]
                                     = [3, 2, 1]
                                  
                                  which is not the same as [1, 2, 3].

                                  https://wiki.haskell.org/Monad_laws

                    • KirinDave 8 years ago

                      > Basically this is (in my mind) what's wrong with Haskell: it's overly concerned with the Platonic ideal.

                      I read this and I think what's got you rustled here is that Monad is such a generic concept. It's quite higher level and so you can do novel things like write functions that don't know how they're executing, just that they are.

                      As an example:

                          -- Config is a typeclass that enables getting a
                          -- keyval from a config. The return type is MonadIO
                          -- because config might need IO.
                          loadTarget :: (MonadIO m, Config a) => a -> m a
                          loadTarget config = do
                              v <- grabKeyVal "host" config
                              w <- grabKeyVal "port" config
                              return (v,w)
                      
                      What does that code do? The answer (if it's written carefully) is that it depends on what the underlying monad is! And that's a good thing, in many cases. If the monad is Maybe + IO, then you have a conditional loader.

                      But if the monad is an array and IO then you can specify many hosts and many ports and this code enumerates them all. If that's passed to a ping function like so:

                          loadTarget config >>= pingHostPort
                      
                          -- alternatively
                      
                          doPings config = do
                            hostPort <- loadTarget config
                            pingHostPort hostPort
                      
                      Well then your code will do the right thing, but a different thing, based entirely on the types alone! And you can generalize this out to even more powerful types. For example, you could write a web app that server side could do local network ports for you (why? you're a maliclious hacker of course!). In that case it might make sense to use the Continuation monad.

                      tl;dr and finally:

                      You say this is stupid abstract stuff, but the folks delivering features to you in the Javascript world disagree. You have generators now, which are a much more real and fair explanation of how to model monads in Javascript than that silly code snippet you posted that doesn't capture the spirit of them at all.

                      What's more, careful application of these concepts leads to libraries which are just better than anything you can have without appealing to generators. A great example of this Purescript-config. Here is an actual (redacted) same of some code I use at work in an AWS Lambda function to read the environment:

                      https://gist.github.com/KirinDave/9af0fc90d005164743198692f3...

                      So I have complete error reporting (full sets of missing keys) just by describing how to fetch the values from the environment. I can transparently switch to any other file type under the covers by changing from using "fromEnv". I only know that there is a key-value store in there.

                      Doing this in OO is really, really hard to get right, because imperative OO code cannot easily parameterize the execution strategy hierarchically without appealing to generics and ad-hoc polymorphism. That's hard.

                      The applicative style adopted here is very simple to re-interpret, because we can parameterize code on monads and applicatives (which explain the execution strategy of this code beyond its coarse structure) as we see fit.

                      You can do that with generators but it's frustratingly hard. Doing it in a truly generic way? Even harder. For this approach, the results are free (and Free, but hey).

                      I can give you other examples of how Purescript makes certain difficult aspects of javascript simply vanish, if you'd like.

                      • dmitriid 8 years ago

                        Thank you for examples.

                        I have one complaint though:

                        > Doing this in OO is really, really hard to get right,

                        Why do you equate FP with monads? Moreover, why do you equate FP with Haskell/Purescript (statically typed FP with monads)?

                        • mrkgnao 8 years ago

                          The use of monads is a side-effect (ha!) of committing to purity throughout a language, and that's what FP is being equated to: pure statically-typed FP.

                          (You can argue about how justified that is, of course. I'm not going into that, but you might want to see what John Carmack has to say[0]. No, he doesn't end by saying "we should all convert to the church of Haskell now", but he does talk about how large-scale game programming refactors are made easier when you're working with no (or very disciplined) side effects.)

                          Monads are not the only way to deal with effects while keeping purity, although they were the first discovered and so on: algebraic effect systems as in Koka[1] (and as simulated in Idris or PureScript) are another alternative. Koka infers effects, so it's probably easier for a C-family programmer to pick up (I know little about it, though).

                          [0]: https://www.youtube.com/watch?v=1PhArSujR_A

                          [1]: https://www.microsoft.com/en-us/research/project/koka/

  • hellofunk 8 years ago

    Here are some relevant discussions related:

    https://news.ycombinator.com/item?id=14870642

advanderveer 8 years ago

What an excellent article: from tech to business, from the human aspect to practical code examples. Worth a read, even if you're not considering Elm.

  • akuji1993 8 years ago

    Not into Elm at all right now and also kind of not convinced about functional programming, yet. But the article was definitely worth a read. I need to slowly open up for FP, I guess.

    • moomin 8 years ago

      I'll make an observation: I write C# for a living. The great thing about that is that it has a truly great debugger. But, as you rapidly discover, it's easier to debug some code than others. For one thing, you want to be able to go back to the start of the function and re-run it. That means that methods that mutate internal state are hard to debug. Also, it's even better if you can follow the chain of reasoning without rerunning the code. This means having a variable for each assignment, rather than overwriting an existing one. Finally, when processing large data lists, it's easier to debug if you have separate variables for logical steps. e.g. Get the employees of Company X (variable) that are managers (variable) and sum their salaries (variable). Trying to debug round a for loop is an exercise in frustration.

      What all these things have in common is: it's easier to reason about the values in a program than the program counter, and that destroying information makes it harder too. And that, to a great extent, is why FP is useful. Even really basic FP in C# or Java.

      • moogly 8 years ago

        Dumb question: Have you tried IntelliTrace in VS Enterprise (sadly only available there)? It tries to solve many of the issues you're mentioning. You can even start a remote IntelliTrace debugging session in production.

        • moomin 8 years ago

          Not a VS enterprise shop, sadly. Don't think I've ever worked for anyone prepared to pay for it! It does sound enormously cool. Does it also handle one of my favourite problems: when a method fails because a constructor parameter was wrong, it's pretty hard to rewind.

          With that said, VS Pro still has one of the best debuggers on any platform. To the extent that I think C# developers sometimes cut corners because the debugger helps so much.

dmitriid 8 years ago

> making very heavy use of Ajax calls to a JSON-based RESTful API

and then

> Want to decode some JSON? Hard, especially if the JSON is heavily nested and it must be decoded to custom types defined in your application. Doing this will require an understanding of how JSON decoding works in Elm and will usually result in quite a bit of code (our application contains over 900 lines of JSON decoders alone).

What a great pragmatic language

  • Albert_Camus 8 years ago

    Author here. As I posted in a different reply:

    JSON decoding is hard relative to what it is like in JavaScript. In your JS code you can just call JSON.parse() and get the corresponding JavaScript object.

    In Elm, decoding is not nearly as easy as it is in JS because every field must be explicitly converted to an Elm value. Depending on the complexity of your conversion from JSON to Elm value (e.g. whether you are just decoding to primitive values or to custom types defined in your program), there may also be a bit of a learning curve.

    As I stated in the post, there is a benefit in doing all of this: your Elm application will effectively type-check your JSON and reject it if it is malformed.

    • dmitriid 8 years ago

      Yeah, it will typecheck it if you're not bored out of your mind writing all the boilerplate and don't introduce subtle bugs https://medium.com/@eeue56/json-decoding-in-elm-is-still-dif...

    • maxiepoo 8 years ago

      Can you not do the same thing as JSON.parse() in Elm by parsing into an unstructured JSON type?

      • mjaniczek 8 years ago

        Parsing into Json.Encode.Value gives you that Value but no way to work with it except, at later time, using Json.Decode.decodeValue on it. And you're back to specifying decoders...

        • maxiepoo 8 years ago

          Seems like you could easily make a library that just parses into some Union type that represents JSON values.

          It's likely a design decision to force people that otherwise wouldn't to represent their data in a more structured way.

  • amimetic 8 years ago

    In exchange you get incredible validation of your JSON and precise error messages about missing fields etc.

    And you don't have to write that bit by hand:

    http://eeue56.github.io/json-to-elm/

    will generate the decoder for you for straightforward cases and give you a great starting point for more complex cases.

    You also get proper ADTs to manage state: not asked/loading/loaded/error something that is tedious and error prone in Javascript.

    • dmitriid 8 years ago

      Yup. There are a bazillion libraries now trying to pretend to make it look like it's easy to deal with JSON in Elm.

      An online converter is definitely not something I would advertise for a "production-ready highly productive amazing pragmatic language to be used in a serious company"

      • dmitriid 8 years ago

        For god's sake, Elm is the only language I know that has _an entire book written on how to handle JSON_: https://www.brianthicks.com/post/2017/01/06/announcing-the-j...

        It's introduction is: "You know how it takes so much effort to produce even the simplest of programs when JSON parsing is involved? Wouldn’t it be nice if you could breeze right on by that step and get on with writing your business logic? This is what you’ll get with The JSON Survival Kit, a short ebook on JSON decoding in Elm."

        Wut

        • Skinney 8 years ago

          And Go has an entire book on how to use the database/sql package. Does that mean the database/sql package is hard? Is React difficult because there is online courses? Is Bootstrap? Of course not.

      • enalicho 8 years ago

        It is no different from using a code generator for anything. Json2elm is designed to _help you_ write decoders, not replace decoders. Once you know how to write them, json2elm is mostly useful for generating boilerplate.

        • dmitriid 8 years ago

          If you need a code generator to generate boilerplate... something's wrong with your language

          • ch4s3 8 years ago

            I suppose you start every new project with mkdir and a few touch commands?

            In all seriousness, ever language has boilerplate. In js it might be error handling and state management. In Elm, its decoding json.

            • dmitriid 8 years ago

              > I suppose you start every new project with mkdir and a few touch commands?

              This is not a valid analogy (and no analogy is ever valid).

              > In js it might be error handling and state management. In Elm, its decoding json.

              Yeah, and Go has trouble with generics.

              It doesn't mean it's a feature that has to be vigorously defended. Especially if it's basically the very first thing anyone will have to do in any web application in a language that is meant for client-side web programming

              • ch4s3 8 years ago

                > It doesn't mean it's a feature that has to be vigorously defended.

                I'm not sure anyone is defending it. At least, I'm not. Basically Elm's compromise is safety, and less runtime complexity then Haskell. The trade off there is boilerplate. As with most things, there is no free lunch. I've stopped using Elm precisely because of these issues, but I understand why Evan made the choices he did.

          • KirinDave 8 years ago

            So... you don't use yo/yeoman? At all?

  • mrkgnao 8 years ago

    The solution isn't more "pragmatism", it's an actual "incredibly powerful type system" like Haskell's. In my projects JSON decoding is just

        instance FromJSON Foo
    
    and that's it.
    • Ar-Curunir 8 years ago

      It's the same with `rust` and `serde`; it's a great feeling!

      • mrkgnao 8 years ago

        I can imagine :) I think over in Rust-land it's something like

           #[derive(FromJSON)]
        
        isn't it?
        • steveklabnik 8 years ago

            #[derive(Serialize, Deserialize)]
          
          but yeah, same difference.
          • mrkgnao 8 years ago

            Ah. That derives both "ToJSON" and "FromJSON", as Aeson calls them. Cool!

            • steveklabnik 8 years ago

              Yeah, and it's actually more general than that: it describes how to serialize and deserialize them generally, so you could use, say, serde_json to get json, or serde_yaml to get yaml.

    • dmitriid 8 years ago

      Nope. The solution is actually "more pragmatism", not "here's a thing that requires a PhD in type theory to understand".

      • mrkgnao 8 years ago

        Type classes are basically interfaces from Java. I'm guessing from your other comments that you know Java (and have read LYAH), so this is actually trolling. The majority of people writing the goddamned Haskell compiler don't have PhDs, they're people using it in industry.

        • dmitriid 8 years ago

          "People using something in industry" is rarely an indicator of anything, really.

          Industry uses everything and anything. Heck, Javascript is (arguably) world's most popular programming language, used everywhere.

          Haskell is used by Chase Bank. J and K are used by banks (J is used by SAP AFAIK). There are stories of Smalltalk running entire factories. There's Active Oberon in a nuclear plant in France. Excel is the world's most widely used FRP environment (unsurpassed, I might add, by anything anyone can offer).

          For almost any programming language you name, I will probably find examples of people using it in an industry somewhere, no matter how good, or bad, or obscure, or popular, or well designed, or badly designed a language is.

          "You need a PhD in type theory" is a hyperbole which I use to say "to proceed to any advanced level in Haskell you will need to dive quite deep into type theory as it's highly likely you will not even understand how most of the libraries you use work. Most of documentation and material around Haskell is riddled with incomprehensible jargon that often assumes the reader is already versed in any number of obscure Haskell things. Haskell has always been and remains a language designed to specifically test multiple theories of language and types design, and will remain such a language for a foreseeable future, no matter how people try to make it 'more approachable' or 'more pragmatic', and no matter how many people 'use it in the industry'."

          • mrkgnao 8 years ago

            > Haskell is used by Chase Bank.

            Also a ton of other financial services companies: Morgan Stanley, Standard Chartered, and so on. Facebook has, in the recent past, hired a lot of well-known Haskellers. Bunch of hedge fund-ish things do too. Jane Street uses OCaml and does a lot of AdWords targeting at Haskell users :P

            I don't know a shred of "type theory" (although I'm interested): you probably mean something else. Monad transformers aren't even categorical abstractions; they evolved from the needs of Haskell users.

            > Haskell has always been and remains a language not opposed to testing multiple theories of language and types design

            There, fixed that for you. Seriously, why don't you make a good-faith effort to dive into Haskell (as opposed to learning just enough of the jargon to be able to troll with apparent seriousness) before lambasting the things that make it a joy to work with?

            • dmitriid 8 years ago

              > why don't you make a good-faith effort to dive into Haskell (as opposed to learning just enough of the jargon to be able to troll with apparent seriousness)

              Why don't you make a good-faith effort to assume that your opponent has actually done some good-faith efforts?

      • Ar-Curunir 8 years ago

        99% of Rust programmers don't have PhDs, I'm sure, and yet they use typeclasses (called traits in Rust) just fine...

      • tome 8 years ago

        At this point you just sound like a troll.

        • dmitriid 8 years ago

          Nope. It's a genuine feeling.

          Basically, in an ideal world we would have a "pragmatically typed" programming language. Basically something in between "weak dynamic typing of Javascript" and "strong static typing of Haskell".

          No such language currently exists, and this makes me sad. It's also hard to come up with a proper definition for such a language :)

          • dragonwriter 8 years ago

            > Basically, in an ideal world we would have a "pragmatically typed" programming language. Basically something in between "weak dynamic typing of Javascript" and "strong static typing of Haskell".

            Souunds like one of the many strong dynamic languages with optional static typing/typechecking.

            • dmitriid 8 years ago

              Yeah. I like approaches taken by OCaml/ReasonML and Typescript. But they are not without their warts :)

          • tome 8 years ago

            > Nope. It's a genuine feeling.

            That's not exclusive with being a troll.

            > Basically, in an ideal world we would have a "pragmatically typed" programming language.

            Yes, it's called Haskell. Or, if you don't like purity, either OCaml or F#.

            • dmitriid 8 years ago

              I strongly believe Haskell is very very far from being a "pragmatically typed" language.

              This reminds me, I need to write a blog post (which is tangentially about Haskell, but mostly about statically typed languages)

iamwil 8 years ago

Having started using Elm for side projects over 3 years ago, the article is pretty much spot on.

Programming in Elm had been a delight, especially when you let go of OOP and embrace functional concepts and practices. On one hand, you lose mental tools that you've relied on, but you gain the other tools you didn't even know existed before.

Where I really disliked about Elm is when I had to encode or decode JSON. It's a giant royal pain in the ass. Also, when you find you have to break out to JS often for libraries you don't want to write yourself, it's not a good fit--as I found out when write a toy interactive notebook to render markdown in Elm.

But for most SPA that just manipulate form data and communicate with the server, it's a pretty great fit.

noshbrinken 8 years ago

Something I find incredibly off-putting about Elm is the evangelical and generally unbalanced tone taken by many prominent members of the Elm community. I almost never come across Elm advocates accepting a valid criticism of the language. The response almost always amounts to "you don't understand" or "yes, but". They spend a lot of time celebrating the compiler's humanistic virtues but seem less clearly humanist in their relation to original thinking or diversity of thought. So much of Elm community dialogue (in talks, in articles, in the Elm slack which I follow daily) is simply those with more experience initiating those with lesser experience into the "Elm way" of doing things. For this reason, Elm feels more like a framework with a domain specific language than a fully qualified programming language. And while it might seem like a gentle introduction to functional programming techniques, I'm not confidant that it really teaches people the concepts themselves nor gives them enough room to think critically about how to apply them. Instead, the task is to internalize and apply the "Elm way". The inability to even acknowledge the unprecedented labor required simply to parse a JSON response is a perfect example of the cultish mentality emerging in this community.

  • KirinDave 8 years ago

    > less clearly humanist in their relation to original thinking or diversity of thought

    So what you're frustrated with is that a group of likeminded individuals celebrates their common point of interest and doesn't make room for you to nay-say them?

    > So much of Elm community dialogue (in talks, in articles, in the Elm slack which I follow daily) is simply those with more experience initiating those with lesser experience into the "Elm way" of doing things

    This could be said of a lot of PL environments. How many Python tutorials stop midstride to browbeat you about how great Python's way of doing things is? Sure feels like a lot to me.

    > The inability to even acknowledge the unprecedented labor required simply to parse a JSON response is a perfect example of the cultish mentality emerging in this community.

    Right but... what do you want? Acknowledgement? My friend, literally everyone here is agreeing it's harder than JSON.parse. No one argues it is "easier." I can't find a handy example in this thread of anyone saying, "Yeah this is great." There are tools to ease this pain, and there are ways to call external JS code that does this.

    At the end of the day though, validating data structures on the wire both structurally and for content is a lot of work. Most Javascript projects don't do it. Hell, most typescript projects just say, 'Well if it breaks it breaks.'.

    By pure coincidence this redacted typescript snippet is up on my other screen:

        function validateTaskArgs(cdata: any, ldata: any): [boolean, IDomainObject1, IDomainObject2] {
            if (cdata === null || ldata === null) {
                return [false, null, null]
            }
    
            const cdkeys = ["key1", "key2", "key3"]
            const ldkeys = ["key1", "key2", "key3", "key4"]
    
            const checkKeys = (keyset: string[], obj: any) => {
                const result = keyset.reduce(
                    (p, c) => { return p && !!obj[c]},
                    true)
                return result
            }
    
            return [
                checkKeys(cdkeys, cdata) && checkKeys(ldkeys, ldata),
                cdata as IDomainObject1,
                ldata as IDomainObject2
            ]
        }
    
    This ugly bit of custom logic just to validate a pair of objects in a larger json datastructure, and even that has bugs. This can't deal with a bunch of problems, but it's just too much pain to actually string together the logic in a composable way in typescript, so I accept this kind of drudgery.

    But earlier I actually linked a much more sophisticated piece of purescript that's about the same size and not only is easier to read and is self-describing (the code to build the objects IS the spec), but it uses those properties to report console errors. You can see it here:

    https://gist.github.com/KirinDave/9af0fc90d005164743198692f3...

    If you want to make Elm better at JSON, that's the sort of stuff you wanna ask for. And folks will rightly be resistant. Because the programming concepts that make that work (free applicative, in this case) are not things the elm target audience has learned about, yet. Elm's leadership is acutely aware of how big a stack of new concepts they're putting on everyone's plate, and they're cautious about offering more.

    • noshbrinken 8 years ago

      Thanks for the code snippets. That actually helps me to understand quite a bit how Elm's JSON parsing situation can be improved within the FP paradigm.

      > So what you're frustrated with is that a group of likeminded individuals celebrates their common point of interest and doesn't make room for you to nay-say them?

      I'm not frustrated with responses to criticisms I've made. In fact, I haven't made any criticisms (except of course, the ones in the last comment :P). So I don't have any experience of anyone not making room for me. But I have observed some smart people with well-articulated suggestions get shut down. It's not that their suggestions weren't accepted but it was the way that their ideas were received. I haven't actually seen someone who isn't Evan C. contribute something significant that isn't "doing X like Evan would do it." In the entire world of this language, there seems to be 1 architect and a community of implementers. Now, there's nothing wrong with being an implementer. I am an implementer. But it seems easy to see that cultures are healthier when there are a diversity of ideas.

      I think that your characterization of the Elm community as a "group of likeminded individuals celebrat(ing) their common point of interest" is actually close to what I'm talking about. It's great when a programming language community is passionate about the language. If people enjoy using that language, it's certainly a good sign. But I wouldn't trust the judgment of a group of people who can't critique what they love and are unwelcoming to those who do.

      The counterpoint here is Dan Abramov and the Redux community. Dan is continually pushing people to understand why they are using Redux and not to see it as a solution for everything. That kind of transparency, and the continual acknowledgement by Redux maintainers that there is more than one good way to do something, is the kind of intellectual honesty that I'm using as a standard in my assessment of Elm.

      > How many Python tutorials stop midstride to browbeat you about how great Python's way of doing things is?

      I couldn't say and it wouldn't change my opinion of Elm.

delegate 8 years ago

Anyone familiar with both Elm and Clojurescript ?

Clojurescript's 're-frame' lib implements something similar to the Elm architecture and is quite pleasant to work with.

How does the Elm experience compare to the Clojurescript experience ?

  • mjaniczek 8 years ago

    Simple: writing code in Elm is compiler-driven development. As the article said:

    > Just start by changing your type definition and the compiler errors will help you find everywhere that needs updating.

    This is how most of Elm development gets done. I change the types and let the compiler tell me what actual code needs changing.

    Clojurescript, on the other hand, doesn't have this "assistant", unless you're using Typed Clojure or Schema (it's been a year or two since I tried that, so can't speak from current experience).

    The Elm Architecture is worth pursuing even in dynamically typed languages though :)

  • hellofunk 8 years ago

    Similarities:

    1) Re-frame and Elm are both backed by a virtual dom (Re-frame leverages React behind the scenes, Elm uses virtual-dom)

    2) Both impose a single app state

    3) Both require user events and outgoing state mutation to be explicitly defined somewhere outside of the context of a view

    4) Both operate on a sort of "game loop" style of processing events and calling view functions

    5) All data is immutable in both languages (though Clojurescript provides explicit mutable structures if desired, which require special syntax so any mutation is evident)

    Differences:

    1) Elm's model is pure FP -- data is passed to a function, and then it calls other functions. An individual view function in Elm cannot independently subscribe to any kind of data event, either a state change or a socket message, etc; it must receive the data it needs as an argument to the function only. Depending on who you talk to, this is either limiting or properly restrictive. It does mean that a branch of your Elm user interface should generally correspond to a single branch of your app state, or that view functions in complex interfaces must take quite a lot of arguments; this is a pattern for which re-frame explicitly offers a workaround (though you can also do it the Elm way in re-frame if you want). In general, these differences lead to more re-usable components in re-frame than in Elm.

    2) Elm has no real asynchronous library for high-level concurrency. Most Clojurescript projects (at least large SPAs) often leverage core.async as an abstraction for large UIs that handle many independent processes simultaneously. Core.async makes clojurescript particularly versatile for accomplishing things in a single-threaded environment as if it were modelled with multiple threads.

    3) Elm has its own compilation story that is separate from the JS ecosystem. The Elm devs are working on dead code elimination. Clojurescript projects are all built on Closure, which provides DCE, compression, and global inlining which can provide some projects notable speedups. The Closure libraries also give Clojurescript cross-browser abstractions for hundreds of common tasks in front-end development that have been battle-tested by Google in all of their web services, so it can greatly reduce development and debugging time for complex apps that run everywhere.

    4) Elm controls JS interop much differently than Clojurescript. The tradeoff is that in Clojurescript you will inevitably spend more time debugging runtime errors, while in Elm you will spend more time developing your JS interop code and testing cross-browser compatibility.

    5) There are many UI libraries available for re-frame because it can wrap existing JS tools (including wrapping UI libraries built for React). There are fewer for Elm, and it's more common to roll your own UI in Elm. There are pros and cons here. I've worked on two large projects in Clojurescript, one which was all custom UI components and another that leveraged polished libraries in the wild. The former took much much longer to make but looked more unique. Having the choice to go either way is a big benefit since project goals widely vary. If you are primarily a developer/coder and not a web/graphic designer, you may be frustrated at UI design in Elm. If you work with a designer, this is not a problem. But if you work alone, having access to a lot of UI tools can free up your time and efforts quite a bit, and Clojurescript has an edge here.

    6) Types: Elm has static types, while Clojurescript offers clojure.spec (which is optional though widely used). Spec is a runtime contract system so you can guarantee that all args passed to functions or setup in data structures meet specified criteria; not just types of the args, but also any other predicates, such as a valid range for an integer, etc. For example, an Elm union type would be a Clojure set, where each element could be a specific data structure with other specs attached to it. However, Elm has proper static types, which are caught at compile time, not runtime. There are benefits of both styles. If you want to tightly catch very specific data aberrations that flow through an app, clojure.spec is easy to use for that purpose; but if you want more general checks before the program runs, Elm would be preferable.

    7) Performance: All of Clojurescript libraries that wrap React (including Re-frame, vanilla Reagent, and Om) offer an interesting runtime optimization that I've not seen in other languages, and is not available in React itself. If the data that a view function requires (either via its arguments or subscription) has not changed from the prior render frame, then the entire view function is skipped without running, since its output would not be any different. What this means is vast sections of your app's code don't even need to run on each render frame, and this is not trivial. In a small app, it would make no difference, but in complex SPAs this can be very significant. Elm offers a library, Html.Lazy, that attempts something similar, though my impression is that most Elm projects do not use it since it can be tricky to explicitly add this behavior; in Clojurescript, it is built in automatically. In Re-frame, they go an extra step by de-duplicating any queries or subscriptions so that multiple views which use the same data context do not query, fetch or calculate the required data more than once, which is then passed to all requested functions. If you are working on an app that processes lots of data frequently, this can be the single selling point of using Clojurescript, as it frees up the CPU to deal with only those things that are guaranteed to require processing.

    Clojure's syntax is very tight and concise, while Elm's language is elegant but more verbose. I have found that there is approximately a 2.5X increase in code size in Elm when trying the same ideas out in both languages.

    All other things being equal, you can probably get a re-frame app going very quickly; if you need to prototype something or get to an MVP as soon as possible, it is really hard to beat Clojurescript compared to any other compile-to-JS language. If however you are going for application purity and a reduction in runtime debugging, Elm (or Purescript or some other statically-typed languages) would be a better fit.

    Ultimately, Clojurescript is a general purpose language, while Elm has a very specific use-case; if you fit into the Elm model, it can be nice. If you need to reach outside that model, it is a challenge.

    • Nekorosu 8 years ago

      Worth mentioning that Clojure(Script) comes from a different school of thought (Lisps) and has its own approach to development called REPL driven development. It allows you to experiment a lot and fail fast.

      Large projects are absolutely doable but require more discipline and experience with the language from developer. For example you can start with a crude prototype and introduce clojure.spec later. When you do this is entirely your choice while in Elm you just have to write function signatures and types/structures definitions from the beginning.

      ClojureScript has a very easy interop with JavaScript. Actually this is one of design goals. Consuming JavaScript library is super easy. For example most of virtual DOM libraries are built upon React. This is good because writing performant virtual DOM with older browsers support isn't an easy task.

      The toolchain has code splitting and dead code elimination. It had it long before Webpack and can even optimize imported JS libraries code. As far as I know Elm still can't do it.

      Another good thing is you can use Clojure(Script) both on the client and the server. This allows you to nearly skip data serialization/deserialization using powerful "transit" library. Actually you can use transit on server with several other languages. So it's not a lock in but it's much smoother with Clojure.

      The libraries ecosystem is rich. For example Clojure(Script) has a mature WebSockets library (it's both server-side and front-end) with any kind of fallback you can imagine.

      • hellofunk 8 years ago

        I agree with much of your points but a few things to add clarification:

        While lisps do traditionally provide repl-driven development and you can do this in Clojure, most Clojure devs I know are not actively working right at the repl. If you do like some aspects of repl-like interactivity, Elm does have elm-reactor which has some similarities to Clojurescript's figwheel, though not quite as mature or comprehensive.

        Aside from the repl, you are right that you can get a proof-of-concept going in Clojurescript extremely fast so if you want to see if an idea succeeds or fails before investing lots of time, you can't beat Clojurescript.

        The Elm toolchain is slowly evolving to include things like dead code elimination (which it doesn't have yet but probably will in the near future). However, I think it will be a long time (if ever) that Elm will support global inlining, automatic variable renaming and some other optimizations that come for free with the Google Closure compiler support. The other important difference is that as the Google Closure compiler adds features or further improves its optimizations, these are mature and widely used and immediately available for free without much (if any) extra work on the Clojurescript team. It's a philosophical difference that the Elm devs prefer to solve these problems on their own rather than leverage existing tools in the industry, but since the Elm dev community is small (much of it is only one person), this means that lots of time must be spent on these solutions at the expense of features more specific to the Elm language.

        Also I think you are right that Elm as a language is a little easier to learn than Clojurescript (though to someone new to FP, both Elm/Haskell style syntax and Lisp syntax might be equally bizarre). But I think that ease is made up for by some of the concepts in Elm that can be difficult to learn (decoding, Tasks, ports, etc), so it's probably a net flat difference on the learning curves between the two systems.

        • Nekorosu 8 years ago

          Thanks for clarifications. I actually pay some attention to Elm evolution and check the official blog from time to time. I was much more interested in Elm several years ago and I even tried to get a job at Prezi (Evan Czaplicki was still working there at that time). In the end I like ClojureScript more. It has a good balance of agility and guarantees. It's more generic. REPL enables exploratory programming and live coding. Btw ClojureScript developers I know do use REPL.

          I probably wasn't clear enough but I don't think Elm is easier to learn. It's quite the opposite. The good thing about Elm is great default libraries for SPA development (or isolated widgets) and great toolchain. You just use what's given and it works perfectly fine. In the end getting productive fast is easier with Elm than with ClojureScript. And it's easy to do all by yourself. Everything is pretty strict and official site has a lot of up to date tutorials. I also like that Evan values simplicity and Elm gets better and better in this regard.

          Every language has its place. ClojureScript is my personal preference. It's just a good fit for things I like doing.

        • mjaniczek 8 years ago

          I believe you can use Google Closure Compiler on Elm JS files with great effect the same as Leiningen uses it on ClojureScript JS files. (Although most people probably use uglifyjs instead.)

          But of course ClojureScript's JS is generated carefully so as to be better optimizable by Google Closure Compiler, and that's not the case with Elm. IIRC, in version 0.19 Elm will change it's JS so that it's better optimizable by uglifyjs.

          • hellofunk 8 years ago

            > I believe you can use Google Closure Compiler on Elm JS files with great effect

            No, you can't use the advanced optimizations from Closure compiler in Elm. This is unlikely to change any time soon because Elm's compiler emits JS that directly violates one of the restrictions that Closure requires for advanced optimizations, namely the referencing of field names as a string.

    • delegate 8 years ago

      Great explanation, thank you !

steinuil 8 years ago

I've used Elm for a while and I don't really see what's the problem about JSON decoders/encoders. Sure, they're verbose and annoying to write if you have a particularly intricate JSON structure, but I don't really see a better alternative for decoding and encoding JSON in a type-safe way, and with Elm's constraints on ease of learning.

More freedom on type-level programming would help, but that would certainly complicate the type system, and the only language I know that lets you fold over arbitrary record types is Ur/Web, which has a richer type system than even Haskell, and I don't see Elm adding things to its type system (other than type classes, hopefully), given that other interesting features were already removed because very few people used them.

unabst 8 years ago

Quick question.

> Want to measure the height of an element on the page at the moment a user clicks on it? Hard, in order to do this we had to make heavy use of event bubbling and writing JSON decoders for the native event.target object that is produced by an onclick event.

What would be considered best practice?

Is it best practice to re-implement this sort of thing? Seems one could easily find vanilla JS code and encapsulate it, or add a helper lib?

fiatjaf 8 years ago

I think JSON decoding is great in Elm. Actually, that may be the best part of that language.

therealmarv 8 years ago

When JSON decoding is hard this is not a minor thing when looking at my RESTful APIs. This is a major trade off. How to deal with it ideally?

  • Albert_Camus 8 years ago

    Author here. JSON decoding is hard relative to what it is like in JavaScript. In your JS code you can just call JSON.parse() and get the corresponding JavaScript object.

    In Elm, decoding is not nearly as easy as it is in JS because every field must be explicitly converted to an Elm value. Depending on the complexity of your conversion from JSON to Elm value (e.g. whether you are just decoding to primitive values or to custom types defined in your program), there may also be a bit of a learning curve.

    As I stated in the post, there is a benefit in doing all of this: your Elm application will effectively type-check your JSON and reject it if it is malformed.

    • Existenceblinks 8 years ago

      > JSON decoding is hard relative to what it is like in JavaScript

      I'm not making a joke but this is a valid point. And if it had ELMON (Elm object notation), things would be more straight forward.

      • mavelikara 8 years ago

        No, that is not a joke. If one tried to convert an Applet to HTML5 and came across a server backend that returned serialized Java objects, parsing that into JS would have been significantly harder than the two lines with ObjectInputStream it would take in Java.

  • enalicho 8 years ago

    Right now, there exist multiple tools for dealing with JSON:

    - json-to-elm http://json2elm.com - swagger-elm https://github.com/ahultgren/swagger-elm/ - elm-graphql https://github.com/jahewson/elm-graphql, https://github.com/jamesmacaulay/elm-graphql - elm-export https://hackage.haskell.org/package/elm-export

    It's worth noting that JSON decoding is not _hard_ as such, but it's harder than other parts of the language. It is more time consuming. Tools like these can help reduce the amount of time you spend hand writing decoders.

acobster 8 years ago

Great article! I have no experience with Elm but I'm much more likely to try it out now. I'm curious to hear your thoughts on this though:

http://reasonablypolymorphic.com/blog/elm-is-wrong

  • walrus 8 years ago

    The core complaint is the lack of typeclasses. If your coding style relies heavily on generics, maybe Elm isn't for you.

niels 8 years ago

When I last tried Elm, I didn't find any really good UI libraries. Also there weren't any good solutions for i18n. Has the situation changed?

  • hellofunk 8 years ago

    Depends on your needs. If you are targeting desktop, there are a couple of UI libraries for Elm. However, they are buggy when you try to test them out on a mobile tablet. I have found that most Elm developers create their own UIs since wider access to JS UI toolkits is not easy. That does mean that you need to enjoy general web design or work with a designer, or be willing to put in extra time when working in Elm.

    • niels 8 years ago

      Yep, Agree. I specifically used elm-mdl and elm-ui. Elm-mdl were not well maintained and elm-ui, while nice, is desktop only.

  • ocharles 8 years ago

    It depends what you mean by "good UI libraries", but there is http://elm-ui.info/ for one. Perhaps that helps?

anon335dtzbvc 8 years ago

"JSON-based RESTful API as its back end" what language do you use for the backend, Haskell?

  • iamwil 8 years ago

    You can use anything that can expose an HTTP port. It doesn't have to be Haskell.

bandrami 8 years ago

I wonder if its successor framework will be called pine.

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection