Introduction to Functional Programming in OCaml
fun-mooc.fr
fun-mooc.fr
I tried Haskell and PureScript and didn't understand a thing.
I tried OCaml and Reason and it felt rather easy to learn.
First: Haskell is a pure functional language, meaning that side effects, IO, keeping state, all that must be dealt with inside the type system, which while undoubtly clean and powerful, it makes it often cumbersome to do things that should be quick and simple. OCaml, on the other hand, is pragmatic. It promotes and facilitates functional programming but it also let's you easily "drop down" (so to speak) to an imperative way to write code. You have mutable variables, imperative loops, you can perform side effects anywhere, etc. As a rule of thumb, you can write fully functional code, yet sometimes when the best way to write a certain thing is imperatively, you can do so with no hassle.
Second: Haskell is lazy, and OCaml is strict. This makes it much easier to reason about performance and makes for much more predictable code. At the same time, it has first-class support for laziness when you do need it, but you have to explicitly "opt in", so to speak. Haskell also lets you force strict evaluation, but you will find it's much easier to build laziness in a strict language than the other way around.
There are also other important differences (like typeclasses vs functors) but I feel these two are the biggest ones.
So you have an implicit dependency graph of evaluation that doesn't exactly match the chain of function call. You may find that some slow code gets deferred until in the middle of some time-sensitive code, for example. You could also have something blow up that's completely unrelated to the calling code, again because of the laziness.
The opposite of lazy is strict, and Haskell does let you "opt in" to strictness when you require it.
In OCaml, you have to opt-in to lazy evaluation.
Haskell does have escape hatches with unsafePerformIO and IORef/STRef
- Laziness can be hard to reason about for newcomers who have really only had experience with strictness
- You have to learn a lot of concepts from category theory right off the bat, because it uses IO and monads. You need to know what a monad is, and almost all monad tutorials are notoriously bad at explaining what a monad is. You also don't know when to stop, when you've learned 'enough' category theory
- The community is full of clever tricks and idioms that aren't really necessary to write haskell code, but a beginner might think they need to know it. Examples: monad transformers, free monads, comonads, lenses, coroutines
- The type system is substantially more complex. Haskell has higher kinded polymorphism, rank-n-types, and a neverending set of extensions that a lot of haskellers use that'll keep you busy learning the rest of your life. Haskell code can also be very polymoprhic, and the type errors can be really confusing sometimes. Ocaml doesn't have these, but you can simulate the most important features: higher kinded polymorphism with functors, and rank-n-types with records
That's why Ocaml is easier to learn and use
Yes
> - You have to learn a lot of concepts from category theory right off the bat, because it uses IO and monads.
No - in particular, you don't have to understand monads to use the IO monad. You just have to understand the IO monad API. That's pretty similar to not having to understand how motors work in order to drive a car.
> - The community is full of clever tricks and idioms that aren't really necessary to write haskell code, but a beginner might think they need to know it.
Yes, and that's the major mistake people may do when starting Haskell. You don't need (and you probably can't) know everything you read about in order to be efficient in Haskell. Just use the simple stuff and live the happy life.
> - The type system is substantially more complex.
Yes, but it shouldn't be a problem. See point above, learn only what you want/need and live the happy life.
(also, Ocaml is an amazing language. It's fine if you enjoy it and don't enjoy Haskell)
The evidence is overwhelming, considering how many people want to learn and write Haskell and how many people struggle with it. If people could ignore all of this stuff they would've figured that out by now, but they can't ignore it to really be productive. I like Haskell a lot btw, just don't think it's very practical, and it's most important contributions to FP are already being adopted in other languages without the additional complexity
Can you recommend good ones? I've read a few and don't feel I can say what a monad is or provide an example when asked.
After you learn the math, that is, the algebraic structure that is called a monad (after, not before) you can watch a few YouTube videos and read some tutorials, until you find the wavy-handy explanation that makes more intuitive sense to you, personally.
I've heard 'Professor Frisbee's Mostly Adequate Guide to Functional Programming' is ok, but I'm looking at the section on monads and the monad laws aren't even mentioned. Which is a huge red flag for me, because without the laws it isn't a monad and you can get really strange behavior if you use it like that. I would avoid reading that book
People write these tutorials because they want to improve their portfolio to further their career, but they often don't even understand it yet -- a perfect example of a perverse incentive.
An example is the guy who wrote "You Don't Know Js" [1]. All of these are completely wrong statements:
- "But what I will say is that a monad is basically a value type."
- "A monad is a data structure. It's a type."
- "Actually, a monad isn't a single data type, it's really more like a related collection of data types. It's kind of an interface that's implemented differently depending on the needs of different values. Each implementation is a different type of monad."
- Says "methods" a bunch of times, there are no methods involved with monads in any way, it's FP
- His example in "Just a Monad" isn't a monad
- "Actually, the Maybe monad is a particular pairing of two other simpler monads: Just and Nothing"
- "Many implementations of a JavaScript Maybe monad include a check (usually in map(..)) to see if the value is null/undefined, and skipping the behavior if so" - that makes it not a monad, it violates the laws. Java's Optional type isn't a monad for similar reasons
- "But... that approach to Maybe is not a pure monad." - there's no pure/impure monads, it's either a monad or it isn't
- "The core spirit of a Monad says that it must be valid for all values and cannot do any inspection of the value, at all -- not even a null check. So those other implementations are cutting corners for the sake of convenience. It's not a huge deal," - It is a huge deal, a whole lot of abstractions are built on top of monads, if it doesn't satisfy the laws exactly then you'll be entering a world of pain you really wish you didn't
I stopped reading the rest but you get the point. You don't want to end up in a situation where you have to unlearn a bunch of this stuff. Btw the guy who wrote the Professor Frisbee book wrote the foreword in this book, so you can see why a lot of this stuff raises red flags for experienced FPers. My suggestion is to read fantasy land and ask questions on an FP channel
[1] https://github.com/getify/Functional-Light-JS/blob/master/ma...
A lot of languages support custom operators, but some really abuse the hell out of them. Googling for <%-/\-%> is very difficult.
So, first of all, I didn't know about this site, and actually it could have helped. I wish it was mentioned early enough (e.g. when first operators are introduced?) in the tutorial I was reading (Learn You Some H.? Not sure, don't remember now.)
Secondly however, I see it still doesn't mention how to spell the operator. Though I suppose in this particular case "fmap operator" could be acceptable?...
edit: Eh: see for example '>>=' and '>>'. Based on the docs, I could only try to call them both "compose", but they need some differentiation in my mind...
You can also lookup a type signature and Hoogle will give you all functions for that type signature. For example, there is only one total function `(a -> b) -> [a] -> [b]` (map) and if you search that Hoogle will show you map, accordingly: https://www.haskell.org/hoogle/?hoogle=%28a+-%3E+b%29+-%3E+%...
Honestly Hoogle for Haskell is better than Google for other languages for finding things.
EDIT: Oh, you can also click to go to the operator docs. For the case of <$>, the docs would tell you that it's an fmap synonym.
If that was your problem, it might be easiest to start by doing things in the REPL (GHCi) where you don't need to be doing I/O, and only start writing standalone Haskell programs once you're a bit more comfortable with the language. But I'm just guessing - maybe you found some other aspect to be a barrier?
People where talking about profunctor optics, monads and lenses, stuff I never heard of.
I find this harder in Haskell than other languages, because I find it harder to progress slowly. In most languages, step one for a beginner is to just hack something together, maybe even using direct for or while loops, switch statements, and the like. Then you can go back and figure out what the language gives you to express what you've done in a better way. Then you can iterate on that basic process, moving up the levels of abstraction, often finding at various points that you didn't need to write any of the code you wrote because a library exists that does the same thing if you call it in the right way. By the time you're reading about the library, you understand the contours of the problem it solves, because you've already done a worse job of solving those problems yourself. I've always had trouble injecting myself into the right point of this step-wise procedure in Haskell, and instead feeling like I need to do a bunch of research on the right ways to do things in order to make any forward progress.
I don't have a good answer, because I wouldn't want to stop building Haskell frameworks that way. But I do agree it's a problem.
The way people talk about them often feels to me like they are language constructs.
I have observed a similar effect that it seems like plenty of people come from an imperative language to OCaml and stay in OCaml longer than say, Haskell. Although I'm not sure why that is but maybe they are much like you and tried Haskell first?
I had the impression the FP hype started with Haskell and so I tried it, alter people started talking about OCaml, so I gave FP another try and liked it.
I've since gone back to Haskell and am finally enjoying it... but I credit OCaml with getting me to a place where I could enjoy Haskell the second time around.
I think it comes down to visual cues. Haskell eschews syntactic sugar and I personally find it hard to read the structure of expression. I found ML to be more explicit.
That and the community around Haskell really loves... clever... constructions. And its type system is more expressive but also complex from a mental model POV.
One of the reasons I'm interested in fb's reasonml dialect of ocaml is that I stumbled a bit going from plan ml (meta-language) to ocaml. That said, I think ocaml too is less complex than haskell. But more so ml.
Join and say "hi"! :)
The ultimate goal of software is to build something useful. The "functional programming tutorial:real world uses" and "claims of superiority:adoption" ratios are suspiciously high.
I started programming in Clojure, Clojurescript, and now messing around with OCaml. The primary language I use at work is Ruby.
The aspect of FP that really helped me even be a better Ruby developer was just having way less shared state. When you have less shared state, and small functions doing one thing with as little side effects as possible it really makes code cleaner and it makes your software behave in much more predictable ways.
I know what some are saying "Well that seems like what you should be doing anyway..." That is true but at least working with a FP language really drives this home and for me did more for code quality improvement than anything else. In fact now when I look at most Ruby code even in big high quality projects I am like why??? Ruby gives you so many ways to do things without shared state (blocks) yet so many choose to set instance variables all over, or store stuff in variables in memory.
I still am not sold on strong static typing such as in Haskell, and yes I get the opinion of what you said about that community. I am not sure though if it is because I am bad at it, or it truly takes more work than it saves you in the future. I just find with FP as in Clojure and OCaml it gives me a lot of gain without ever getting in my way. So it is worth trying, and even if you keep working in a large imperative language, FP experience will help you not fall into the holes of large shared state.
I think viewing this as a fallacy is a bit of a straw man. There are very few people who believe in a "Right Way (TM)" to write programs (although those people are probably over-represented in the comments section of online news posts about functional programming).
> Maybe a trap for contemplative people?
FP is hardly unique in this regard. Were you around in the 1990s? Recall that the OOP crowd got pretty philosophical there for a while, with elaborate treatises on software organized around some obscure book about architecture. ;-)
> The ultimate goal of software is to build something useful.
It's worth noting that the ultimate goal of functional programming (as a research field) goes beyond building software; (typed) lambda calculi are an entire alternative model for studying computation. The science of computation has many applications beyond software development.
Interesting point about software having uses outside of programming a machine to do tasks. I can see an appeal there.
I disagree; what spectrum are those on opposite ends of? Encapsulation, polymorphism, and inheritance are entirely compatible with FP, and immutability is entirely compatible with OOP. Functions and objects are equivalent. Most mainstream OO culture is centred around procedural programming, but it's the procedural parts that oppose a functional style, not the OO parts.
I also am not entirely sold on funfunfun's characterization, but if you think of this in terms of cultural hangups that cause excesses rather than concrete technical contributions then it makes some sense.
The excesses of OOP culture are distinctly different from the excesses of Functional culture.
OOP in excess looks like software architecture books with quotes from Alexander and FLW, programming as artform, etc.
Functional in excess looks like really bad descriptions of category theory promulgated by writers who couldn't tell you what group theory is good for.
So if we want to characterize these ways of programming in terms of what harmful excess looks like, then they are on sort of opposite sides of a spectrum. Where one side considers programming pure art and the other pure logic.
I'm still not sold that this is a good model of reality. But if you focus on excesses rather than useful insights, I can see what funfunfun is saying. I think.
FP is great for many little things you might have to do. But for larger architectural considerations, I still find notable advantages to creating custom classes that have their own public API, private internal implementation, and that manage their state internally. In the absence of this, you have lots of functions that are basically sitting alone with their pure inputs and outputs, and to manage state requires more effort, not less, in a lot of situations. Each of these functions has to receive the thing they are acting on, and a separate group of functions manages the actual mutation, acting as "controllers" between these more pure functions and the actual stored state. The result is a less elegant program structure sometimes.
FP enthusiasts like to say that "it's better for many functions to operate on common data structures than it is to have many custom data structures with their own functions." The problem in my experience is that often, these pure functions are only serving one purpose geared for a particular data layout, and thus they might as well be coupled to it; the lack of a backbone in larger FP architectures can be frustrating. In Clojure, you have maps, and all maps behave the same. Great. But not all maps are actually the same. Each map ultimately has its own structure, and these functions that operate on a particular map are really operating on a particular structure a lot of the time. So you end up with increased verbosity and more difficult-to-follow code structure than if you just combined the data and its functions in one place: a class. The record-protocol idea attempts to alleviate this, but I don't feel it really gets to the heart of the issue.
I think OO and FP each solve problems in equally meaningful ways and different tasks are better suited to one or the other. I particularly like languages that don't force you into a particular model. Java and Clojure are quite opinionated (in opposite ways) and thus often inflexible for certain tasks. My favorite language for blending these techniques has become C++ but I don't get to use it as much. It offers true FP-style programming with constructs like std::function. I wish Java had gone as far as C++11 did; the Java lambdas and the requirement that they can only be type defined using a static interface is not really helping to adopt the paradigm.
In OCaml, you usually do that with modules. For instance, you can easily write mutable or immutable datatypes that hide their internal representation.
> My favorite language for blending these techniques has become C++
I also like programming languages that embrace several paradigms. The problem with C++ is that it's a very complex language. Compared to C++, OCaml is a piece of cake.
Ocaml has declarative OO programming via it's module system. Typeclasses and Ocaml modules have equivalent expressive power, so you'd achieve that with typeclasses in Haskell. But Ocaml's type system is much simpler and more explicit, which makes it much easier to learn and use
> The ultimate goal of software is to build something useful. The "functional programming tutorial:real world uses" and "claims of superiority:adoption" ratios are suspiciously high.
Yes and no, what they're really getting at is declarative vs imperative programming and how declarative programming makes managing state and reasoning about code much simpler. In imperative code you often have to step through mentally or through a debugger to really understand what's going on. It also really simplifies concurrent programming (ie map-reduce).
You can have it both ways -- declarative OO programming exists with Ocaml's module system. You can even write declarative OO programming in Java, using 'static' classes (or non-static but only used to configure so it's essentially immutable), 'final' everything, and immutable data structures
Referential transparency, mutation, non-termination, coinduction, memory pointers, linear types, coroutines, continuations, blah blah blah. All of these things exist, and more.
Most PLs are biased in one way or another, many don't even let you reason formally about those things (or allow you to reason informally).
But at least if you're a working programmer, especially if you work to map an existing human domain to software, there's always the fixed point of Curry-Howard to hang on to.
At least that’s been my experience in my years of writing professional, money-making software in it.
Guess I know how the rest of the world feels now watching American tutorials.
Try sudo kill-ing coreaudiod
(disclaimer: I haven’t seen the video in question, but I had a similar problem with online videos in general)
https://stackoverflow.com/questions/179492/f-changes-to-ocam...
http://web.archive.org/web/20080410181630/http://research.mi...
How’s F# adoption?
Based on inferences it has a small, enthusiastic and talented userbase and has still language core developers bankrolled by Microsoft.
There's been some worried discussion whether Microsoft wants to continue investing in it or not, e.g.:
https://www.reddit.com/r/fsharp/comments/91gg6j/is_microsoft...
Personally I hope they would continue the investment.
For me, at least, I've had negative experiences with Mono-based applications on non-Windows platforms (e.g. Keepass), so that is scaring me off from wanting to build anything on .NET.
Data science-y stuff is our next target, starting with finishing out our REPL support for .NET Core. It's not fully functional (turns out it's a ton of work!), but when completed it'll have support for ingesting packages in a REPL session, and it'll work with libraries such as ML.NET.
I appreciate everything the open source community does btw. It is just a little frustrating that there is a really cool language and ecosystem that I'm only making glacial progress in.
The main benefit of an industrial language is the entire toolchain including IDE with code higlighting, intellisense capabilities, debugging capabilities, etc. To provide these for F# in Visual Studio requires continuous investment.
Hence, given the not-entirely-committed feel MS is emanating just now for it, if they don't feel supporting F# as a first class citizen on visual studio is worth the investment, they might orphan it to to open source developers. Who probably would hold good care of the core facilities, but would probably lack resources and interest to provide an entire industrial strength stack. I do hope this is not the scenario, and Microsoft will continue investing in the language as a first class citizen on visual studio.
(lots of language stuff at the beginning, but there's a tools and "what we're thinking of next" section afterwards)
To be painfully blunt: The language is really nice as it is. I would prefer it would expand on the bolted-on batteries included utilities rather than new contrived ways of code golfing.
What is critical from industrial point of view that the fundamentals are now and "forever" rock solid. Not how many new language features it will gain.
See C for reference. Nobody uses C because it gets new ways to write shorter code. People choose C for stability[0], not because it's horrible language or because it's a beautiful language.
F# would be fantastical, if it would ossify like C. Then I could write it for the rest of my programming career.
[0] Yes, C moves forward, but with a geological pace
* F# for unit testing
* F# + Giraffe or Saturn deployed to Docker containers on .NET Core (people tend to love the succinct routing and that it all runs on ASP.NET Core in Docker)
* F# for internal tools to ingest and manipulate data, then put it into another place in a much more sane format
* F# with FAKE for a right proper build script
* F# with Azure Functions to perform some scheduled task or something like that (it also helps that Functions is dirt cheap)
Good luck!
However, I have messed around with data manipulation in F# for sports betting, and it's been exceptionally pleasant.
Love the work you guys are doing on the F# team.
Thanks for the compliment! And I hope that it all goes well with you.
[0]: https://aws.amazon.com/blogs/developer/f-tooling-support-for...
[1]: https://read.acloud.guru/comparing-aws-lambda-performance-of...
I recently was trying to decide which language to pick up next: Lisp, OCaml, F# or Haskell (I know a bit of Lisp and Haskell already, as well as some Standard ML).
It came down to two: OCaml and F#. The former has poor support in Windows (work machine), and the latter has less poor support in Linux (home machine). So I decided I'll probably go with F#. I've been told the syntax is quite similar, so how difficult is it to translate an F# program to OCaml. Are all the patterns the same that one can do a more or less 1:1 translation while still being canonical?
The opposite is also true, F# does not have ppx, OCaml's object system, GADTs, polymorphic variants, functors, first class modules, extendable variants.
On the other hand, F# has computation expressions (essentially extensible syntax sugar for certain kinds of operations), as well some nice extensions to the pattern match system.
That's not to say one is exactly better than the other, they just focus on doing different things.
There are still a few deployment scenarios where only C#, VB.NET, C++, JavaScript get to play and F# gets no entry.
A few F# features still don't work on .NET Core, like REPL and type providers.
Speaking of which, type providers is the king of F# demos, instead of discussing how and if ever .NET Native devs will care about F#.
Edit: https://twitter.com/dsyme/status/981799285925842945?s=20
I fell in love with this language in University long ago (in the 90s), but I have never seen anyone use it in the wild.
Utop [10] and ocaml-jupyter [11] are the best choice for experimenting, browsing the API and testing some small things.
[1] https://dev.realworldocaml.org/toc.html
[2] https://ocsigen.org/lwt/manual/
[3] https://github.com/ocamllabs/ocaml-ctypes
[4] https://github.com/mirage/ocaml-cstruct
[5] https://github.com/thierry-martinez/pyml
[6] https://github.com/owlbarn/owl
[7] https://bucklescript.github.io/
[8] https://dune.readthedocs.io/en/latest/quick-start.html
[9] https://github.com/inhabitedtype/angstrom
- Lack of automatic multicore scheduling, this is going to be solved with OCaml multicore project [1]
- Lack of updated GUI libraries. There is LablGTK [2], but it stuck to GTK+ 2 version. The main developer started [3] to work on GTK+ 3 version support, but it is still at the very beginning.
- Bad Windows platform support [4] (grep for "Windows" word at this page) and [5]. With upcoming opam-2.0 [6] release the support will be slightly better though.
[1] https://github.com/ocamllabs/ocaml-multicore
[2] https://github.com/garrigue/lablgtk
[3] https://github.com/garrigue/lablgtk/issues/2
[4] https://caml.inria.fr/mantis/roadmap_page.php
[5] https://github.com/ocaml/opam/issues?utf8=%E2%9C%93&q=is%3Ai...
[6] http://opam.ocaml.org/blog/opam-2-0-0-repo-upgrade-roadmap/
Another popular solution in the OCaml community is to stop using exceptions and use Result instead: https://ocsigen.org/lwt/3.2.1/api/Lwt_result
Because the exceptions aren't statically shown at each usage point, it's hard to know what failure modes you should be watching for.
It's not going quickly nor easily, and there is no real help from Jetbrains IDE.
edit: it's a nightmare of dependency hell to install merlin, I'm giving up for now.
OCaml has a relatively immature ecosystem, but merlin is a great tool.
https://gist.github.com/nraynaud/deeacbb67c88646fd6754edba6e...
Sorry, I'm a bit abrasive, but I spend a lot of time with crappy tooling of all kind, sometimes I'm envious of monoproject people (and then I remember that actually I don't do well in monotony).
I’m likely to try this out.
2: I do mix both paradigms, so I’m not stuck without imperative features. That being said, there are times where I feel like my attempts to move a concept into FP forces me to make some less than ideal choices to fit it into the paradigm. If those are in inner loops, I forgo functional programming to do precisely what I need. (For example, managing exceptional cases or heterogeneous actions can be awkward.)
3: I would have used them more. I’ve always been a fan of comprehensions and map c/o Python, but my code would have been more efficient and concise had I started earlier. The additions of cppitertools, range-v3, <algorithm> and <functional> make functional programming both easy and efficient in C++ in ways it was not prior to C++1[147]. I think Python made fp accessible by putting it in an imperative language which is concise and readable.
It's cool that cpp is 'flattening' through fp idioms, you can avoid its complexity without leaving the country entirely.
I'm ok with imperative code as long as the mutable state doesn't 'leak out' into the rest of the code. Quicksort is an example of that -- it's impure in it's implementation but essentially pure to the end user, because you can call it any number of times with the same input and it'll always return the same output
Or if it's some looping construct, recursion might be ok but if it isn't readable or you don't get tail-call optimization, then a for or while loop makes more sense. I do prefer map/filter/reduce/forEach or recursion, I think it's more readable and it avoids the indexing class of errors
Shamelessly plug: I'm working sketch.sh [1] as a learning project for ReasonML. It's a interactive playground (like IPython/Jupyter) for OCaml and ReasonML. It's provide inline types and evaluated values. Give it a try if you're learning OCaml/ReasonML.
[1]: https://sketch.sh
I work with Haskell fairly regularly, and have experience with a few other languages (Python, Racket, C/C++, Java, etc.), but I definitely agree with this sentiment. I am not a fan of OCaml's syntax at all.
I haven't had a chance to check out Reason yet though, so maybe I'll spend some time playing with that soon!
https://wiki.haskell.org/OCaml has some trivial examples
Two things that I find particularly annoying are constructors (which don't have the same syntax as funciton calls) and the multitude of shift-reduce ambiguities that don't do the thing that you want. For example, in the following code the W gets parsed as part of the inner match:
match foo with
| X -> match bar with
| Y -> 1
| Z -> 2
| W -> 3
A lot of this could be remedied by having more expression terminators. ReasonML uses C-like curly braces but even a simple "end" token to close the match expressoin (similar to what Lua does) would already be enough. match foo with
| X -> (match bar with
| Y -> 1
| Z -> 2)
| W -> 3
match foo with
| X -> begin match bar with
| Y -> 1
| Z -> 2
end
| W -> 3No offence intended, but English, my ass!
Although quite proficient, English is still a second language to me and this accent makes it quite unintelligible. I hope everything will be available in written form as well.