Selecting a platform: JavaScript vs Elm vs PureScript vs GHCjs
mutanatum.com
mutanatum.com
Koka https://github.com/koka-lang/koka is a very interesting research language that focuses on explicitly specifying effects in types. It even deconstructs Haskell's notion of purity into "throws exceptions" + "may never terminate". Unfortunately, the code generation (which BTW exists for both JS and C#) is uh… not very good.
But honestly, I haven't felt the need to use a functional language in frontend development. JavaScript (ES6) is good enough, and it's actually supported in browsers, so you can develop without any compiling, and only use Babel to make production builds. If the project is not small, use TypeScript to add some type safety.
This has basically been my experience with Elm. But don't say that Elm is boilerplate heavy to any elm-acolytes, they will stick their fingers in their ears and go "nananananana"
Other things can't be solved at all, since Elm is so tightly coupled to TEA.
Or in Greenspun's Tenth Rule Of Programming terms: Any sufficiently complicated Haskell/PureScript program contains an ad-hoc, informally-specified, bug-ridden, slow implementation of half of a Python VM trying to share state with an ad-hoc, informally-specified, bug-ridden, slow implementation of half of a JS VM trying to communicate with an ad-hoc, informally-specified, bug-ridden, slow implementation of half of a .Net VM.
Anyway I like typescript now too.
So we're back to FP... =P
Do you have other insane JS semantics in mind?
And yeah, F# looks brillant. Just wished it was not a .NET thing. Fable could interest me but it's just like bucklescript/reasonml: there is no momentum, no community, no libs. It would be a big leap of faith to heavily invest in it as an individual.
I generally like the FP approach with imperative escape hatches. Need more performance? Some code is actually more elegant in imperative form? Go for it. F# can do it, scala can do it too (unfortunately with some crappy java compatibility concerns to deal with)
Then what? OCalm is the other possibility, but the appeal of a larger ecosystem and easier mobile target by xamarin is what put me into F#.
>> Early preview of @reasonml bindings to ReactJS. Compiles to regular JS so interop/debugging is easy. Haven't been this excited since React!
pure FP is just like pure OO
As someone who did OO for ten years and is doing FP full time, this statement is absurd. Why constraint ourselves?
You are not constrained in any way by FP, you just have to make everything explicit so programs are easier to read and reason about. There's nothing I can't do in a pure FP language. The world is not pure,
The world is not a touring machine either, does this argument have more substance or is it just an offhanded comment...? our programs don't become ANY better because of the IO monad
Do you actually have any experience with functional programming? Non rhetorical...it's such an odd thing to say.Programs absolutely become better when you encapsulate IO in a monadic context. The entire point is to demarcate what functions interact with the world and which don't. This makes testing easier, reading code easier, composition easier. What functional language do you have experience with that would lead you to these bizzare conclusions? :)
This is 1. a tradeoff , 2. subjective. Words like 'bizarre' aren't really necessary, and come off as feigned surprise (passive aggressive).
You think a function being easier to test (without reaching for extra tools like mocking libraries, presumably), justifies needing to write and understand 'monads' in order to interact with the real world.
Another programmer finds myObject.doExplicitThingToWorld() far more expressive and easy to understand and is very familiar with the mocking tools/practices they need to test that.
As for composition ... well composhmishion. It's so rare you can actually compose your business logic/app-code (beyond the original use-case you wrote the thing for, which you might well have chosen to write in a 'compositional' way).
It's neat for frameworks and libraries to create composable stuff but it just doesn't come up as often as we'd all like to hope in our day-to-day code in my experience. And if you really want to compose something OO absolutely has ways to do it.
(So composition is not the big win over OO some FP fans make it out to be IMO).
In comparison, `IO` is a type (actually a "type constructor", AKA a generic). Since many useful functions (e.g. `readFile`) return values of this type, it's pretty hard to avoid; although you can probably get quite far by ignoring types completely and having everything inferred.
In that sense you do need `IO` to write, say, an API server; but I think that's pretty reasonable, since you want you server to read input and write output. Whether or not `IO` is a `Monad`, `Functor`, `Monoid`, `Alternative`, etc. doesn't really matter for that.
Of course, you can always wrap your code in `do { ...; }`, cause all the side-effects you like, and ignore the fact that behind the scenes it just-so-happens that you're using a monad; after all, that's what pretty much all imperative languages do!
More seriously, I think that the Haskell community should try to avoid this habit of saying "the Foo monad", when "a Foo value" would be more accurate, since I think it can confuse new users about types, values, type classes and instances. In particular it can give the false impression (reflected in your comment) that writing simple programs in Haskell (or FP in general) requires learning fancy things like monads. In reality, that's no more true than claiming that before writing a Web site in PHP you have to learn inversion-of-control containers. They're a commonly used framework/scaffolding, but there's no reason to confront them before you've written a few applications without, and hence are able to appreciate why they exist and what problems they do/don't solve.
This mostly seems to be a problem for IO and Maybe. In particular, "State monad" and "Reader monad" don't have this problem, since their values have more widely used names like pairs/tuples and functions, respectively. Likewise, I've not seen this happen with other type classes, like "it returns a Maybe functor", or "I need to combine two List monoids".
In the case of Elm, what I dislike is that everything ends up being a Task/Side Effect/IO. Even asking for a value to a Javascript Money library. That, to me is an unacceptable tradeoff.
Another programmer finds myObject.doExplicitThingToWorld() far more expressive and easy to understand
By definition, I can reason more about my function returning a monadic context than I can about theirs. I can get free theorems from my function, I can see clearly if I'm doing both input/output, (or just output), and I have laws to help me understand how I can compose this function with others. None of that comes for free when side effecting freely.To me that's not a good enough reason to write my code in that style, when to me (subjective), OOP models the world in a way that is more expressive, I/O is not a thing I need to run scared from, and the methods I write that mutate the world do so clearly enough to me, just in their naming.
> myObject.doExplicitThingToWorld() [is] far more expressive
But if I put the word "than" after that, consider what you would type next. Are you arguing that "myObject.doExplicitThingToWorld()" is more expressive than "doExplicitThingToWorld(myObject)"? Because you could write code like that in the FP world, and it's basically the same as the OO world, except (assuming immutability) you'll also know that myObject is not changed after that call.
But runT1ME is talking about how the FP equivalent would come with more information ("I can see clearly if I'm doing both input/output, (or just output), and I have laws to help me understand how I can compose this function with others").
This added information is not subjective. There objectively is more information. The stuff you conveyed in your OO code by naming a method "doExplicitThingToWorld()" can also be conveyed in FP code by naming a function "doExplicitThingToWorld()", but the OO world misses out on the information that monads bring (and monads are just an example of this).
Sure, saying that "more information is not necessarily a good thing" is true. But are you arguing in favor of having less information? Because in that case, why isn't procedural-style programming better (subjectively) to you? After all, one could make the case that OO pollutes code with unnecessary information (classes and hierarchies) and that just like how FP overhypes composition, OO overhypes inheritance
Yeah the overall win is what's subjective. To many, programming in a functional style, and embracing concepts like 'monads', is not appealing, regardless of that additional information.
There are lots of things we could do to add more information to our programs. We could write tooling that religiously enforces a certain amount of commenting for example. which would be 'more information'. But it's debatable/subjective that adds value overall, given the cost to developers.
> But are you arguing in favor of having less information?
Short answer, no, it's the manner in which it's conveyed, and the hoop-jumping required to do so that constitutes the subjective element.
Well hopefully said programmer knows their mocking/practices will not be nearly as exhaustive and fully understand the price they pay for a little initial gain in expressiveness.
Perhaps correctness is of low enough importance it doesn't matterin their case, but I would hope an honest analysis is done.
That said, most of my work is "doing stuff", not data manipulation, so perhaps that's the difference. Also "OOP" for me is C#, which has plenty of FP features (though F# style records and `with` syntax are annoyingly missing); if I was comparing Haskell to Java 1.0 code then I could imagine seeing things differently.
imo, types shouldn't be used to compensate for the lack of separation of concerns (simple data manipulation VS IO)
in my experience either IO absorbs everything but the most trivial trinkets that I wouldn't ever mess up anyway
You mean you never mess up business logic? IO should only really be used when you need to be interacting with the world, IE, network calls, UI, etc."Difficult" code is stuff that involves things like distributed transactions and whatnot. All that stuff has to be bundled in a big wad of IO anyway and so Haskell has nothing to help out with that in the first place (if there was a language that could, I'd be all over it). At least that's my experience. YMMV.
I half-expected this in my Haskell static site generator real-world-learning-project-of-sorts (reading files, writing files, "calling some smarty-pants composed combinators in between", nah not really just functions all the way down.. and somehow such a simple tool grew out of all proportion soon enough with ever more powerful markup notations to support etc pp..) but was profoundly surprised just how little of the code-base ended up in the IO monadic context and how much logic ended up as purely functional modules. Pleasantly eye-opening! (Sure even the IO monad is "purely functional" at least for the programmer though not the final assembler, but y'know-what-I-mean..)
Just an idle anecdote though
Almost all business logic is pure, and it can get pretty complex in my opinion.
At least that's my experience building a lot of single page apps. That could be seen as a weakness of SPAs. With fully separated pages, it's easier to mix technologies.
TL;DR: The 'impurity' of the world is an illusion created by our brains.
I have a physics degree and I completely disagree. The world IS pure.
Think of it like this: Time is the successive application of a pure function that takes the old universe as an argument and returns the new universe. All science models the real world like this. This is the foundation of mathematics. Nowhere in physics or maths do we mutate anything. Even quantum applies this perspective. This is what leads to the many worlds interpretation of quantum.
What's actually going on is your brain is such a powerful illusion machine that it has convinced 'you' that the world is composed of 'objects' that 'change'. This is entirely inside your brain. It is a side effect of the perception process inside your brain. It does not mean the real world is actually composed of 'objects' that 'change'.
This illusion is so strong that we then go on to model the real world inside computers with 'objects' that 'change'. This is a mistake that leads to no end of problems.
Once you realise the world is immutable, and that the process of time is a function, and that what we see as time is actually a succession of immutable 'universe-values', you then start modelling your problems like this. And then you realise what a better fit FP is for modelling the real world. And then you realise why science models the world using mathematics... that is functions and values... which are immutable.
When I argue this with OO developers I ask them "Is the real world mutable, or immutable?" When they answer mutable, I then ask them to go back to last Tuesday and change their lotto numbers. They can't. It's almost as if the past.... is immutable! And the present becomes the past the very moment it comes into being. And the future is unknown.
The present also cant be changed. We can affect the future by using our muscles to apply 'forces' to the 'universe-value' thereby changing the way the time-function of the universe returns the new-universe value (the future), but we cannot reach in and alter any of these immutable values directly, not the past, the present, or the future.
"Many worlds" is more philosophy than physics, isn't the most accepted interpretation, and even going down that path leads to lots of edge cases that could still imply impurity.
Besides, if the world is pure, and OO is part of the world, then OO must be pure too so what's the problem?
Also, like the grandparent comment said, ClojureScript is a very nice language too, with powerful tooling.
The nice thing about ScalaJS and ClojureScript is that they both can run on the browser, and on a JVM backend. Scala also has an LLVM backend that is being actively developed, but on its early stages. (ScalaJS is very mature and performant)
I converted our Node codebase to Typescript and had no problems at all. I don't know much about Flow, but what is it that makes you think it's easy comparative to Typescript?
https://hackage.haskell.org/package/servant-purescript
I'm using this with Halogen and it works great
Was this really necessary? I stopped reading your post after I saw this because I got bored and no longer take you seriously.
We're talking functional in the context of JavaScript, I think ClojureScript deserves more of an explanation on why it doesn't even qualify. That is, if you're trying to hold to the "neutral as possible" thing in the beginning.
Sorry, but your amazing, functional, pragamatic system is INVALID. Because I have deemed it so! Good one!
Those are the font sizes I used as a 4th grader trying to write a four page essay with 500 words.
[1] http://www.cs.princeton.edu/courses/archive/fall14/cos109/
[2]https://www.stallman.org/archives/2016-nov-feb.html
They'd be even nicer if the text was inverted, though.
I quickly stumbled across this:
https://github.com/slamdata/purescript-halogen/blob/master/G...
This is pretty much the Elm Architecture, except fully understanding every detail there requires the reader to be conversant with some pretty hairy concepts, including free monads and the Free functor. That's not just something that's under the hood. It's built right into the user-facing API, and the user even has to tend to this free construction when writing `eval` functions by manually writing lines like `pure next` or `pure (continue value)`, which occur in the first example.
It seriously concerns me that the user will have to write lines that they couldn't truly understand without understanding free monads, because frankly nobody new to Haskell and PureScript is going to be comfortable with free monads until they've spent some serious time with the language.
I happen to be very well-versed in Haskell so this stuff is not an issue for me -- in fact the use of Free here is not only clever but kind of beautiful -- but I wouldn't really want to onboard someone onto a PureScript project who'd never used Haskell or PureScript before and didn't even know what a functor was. "Why do I have to write this `pure (continue value)` line? What does that mean?" -- "Oh boy... grab a seat and clear your schedule."
Some boilerplate for JSON decoding looks pretty tame in comparison, to be honest.
Haskell does this by representing IO actions as values, eg. "IO String" (something like "IO<String>", for people more familiar with that syntax), representing an IO action that, when later evaluated, will yield a string that you can use in subsequent actions.
Haskell's "main" (entry-point) function is a value of type "IO ()" (IO of a value representing nothing useful; you don't get anything out of it, so you just have a nothing placeholder). When a Haskell program is run, it ostensibly (hand-waving here) takes that IO value and interprets it, performing the action IO.
You have functions like putStrLn (print a line) that takes a String, and returns an "IO ()" . Accordingly, if you have
main = putStrLn "Hello, World!"
... you have a value that lines up with the expected return value of main "IO ()". More complicated examples, like main =
readLn >>= (\x -> putStrLn ("Hello, " ++ x))
... are doing the same thing (checking the types line up), just with more moving parts (the Monad thing you mentioned, which we're using to relate the "read line" function with the "print line" function).The key bit is that it's just "IO Blah" values standing in for IO actions, and like jigsaw pieces, these IO-returning functions only fit together where the types line up, restricting how you can use them.
Monads are tangential to this; the type-checking of that jigsaw puzzle is what matters here.
Edit: Also, why do fewer and fewer sites have an RSS feed? :(
Bit sad, that. Somehow it happened that a critical mass of bloggers/readers moved over Google Reader while during the same period all those social-feed web apps kept gaining momentum, then Reader shut off.. things can change like that over a decade or so. A 20-year-old today was perhaps 7-10 when XML feeds were hot currency. I'm still content with my numerous working subscribed feeds in Thunderbird though, can't catch up as it is.
It was briefly mentioned in another comment, but was written off for it's lack of momentum, community, and libraries.
I'll respond to these important concerns one by one:
Momentum: The fable project began last year. There are still active discussions about tweaks getting it to v1.0, but already there is enough there to make complete applications. There isn't hackernews/reddit hype, but the gitter is busy and there are some using it in production already.
Community: I assume that the concern with the community is that it isn't large. F# has never had as large of a community in any of its incarnations compared to mainstream OO-first languages, but the community has always been there to help me out and I've never gone more than a day or two waiting on an answer to a library/language question (most fable related questions are answered very quickly on gitter). The latter fact alone makes it more impressive to me than some other communities. Community is also important because of the amount and variety of libraries that are created for a language. I'd argue that this isn't as important for transpiled languages as it is for separate compiled languages, where for example a ruby library can't be used in python, but I can use any javascript library in fable.
Libraries: Library usage is interesting when talking about transpiled languages, and mostly depends on the Dynamic and Foreign interfaces available. Because of Fable's dynamic abilities, I can dynamically call any javascript code I need to. I use this for a couple of libraries in the React-Native application I'm building using Exponent. The other method is Foreign interfaces, which is fantastic in fable because of how the F# type system (and inference) works in the first place. There is a utility to convert from typescript definitions to fable definitions (some will need tweaking afterwards for greater type safety) which adds a ton of libraries itself. I'd argue because of that fact that Fable has more libraries than any other functional-first transpile-to-js language!
Here is a example repo I put up earlier with full-stack F# with live-reloading on the client and server. https://github.com/Banashek/Universal-FSharp-Samples
I'm using a setup like this on a current project deployed with docker-compose to digitalocean.
Full-stack F# has been nice to work with so far.
One really nice experience I had was being able to reuse viewmodels and their validators.
Android/iOS -> Exponent
Web -> Fable-Elmish
Server -> Suave
I had viewmodels and partial validation functions which could sit in a shared folder and be referenced by all of the projects.
I change it in one place, and I can update all 3 instantly. All of the rest calls depend on the types definition, so I didn't have to do anything after adding a new property to the type and saving the document. The server reloaded, the frontend reloaded, the mobile app reloaded.
I'm not sure If I'll keep it that way, as coordinating production updates is something I haven't fully figured out yet, but for now it's neat to explore.
Would I recommend someone who doesn't know functional programming to switch their applications to fable? No. Would I recommend fable to someone familiar with functional programming looking for a better way to manage their ui? Yes.