I decided to tackle this year's Advent of Code in F#. It's not only my first time using this language, it's my first time ever using .NET. I don't know anything about using the Common Language Infrastructure at all. I was expecting a rough, slow start as I got used to a brand new environment, and I'd be able to write about the process as I learn how to unstick myself.
Hasn't happened. Turns out F# is great, highly easy to use, and I haven't gotten stuck. In fact, it's probably the quickest I've made it from "brand-new language" to "solved an AoC-type problem", ever. So I'm just going to write that post instead.
I've certainly gotten stuck on the problems, and I'm not necessarily pleased with my implementations so far - all could be optimized - but my issues have nothing to do with F#. The documentation is thorough, and I'm generally a single Google away from the .NET function I'm looking for. Lots of documentation for C# will apply too if you can't find anything for F# specifically - my only trouble is not knowing any C# either, but it's Java-enough to follow along!
Now, to be fair, I don't think you'd have the same experience if it were your first MLish language. But having even just a very little bit myself in Haskell and OCaml I found nothing surprising here.
Almost everything I've needed I've found right on the Tour of F# and the Language Reference covered everything else.
There's also this great website, downloadable as an offline ebook: F# for Fun and Profit.
Some things I like:
Pipes:
util.applyClaims fileName
|> Seq.filter (fun el -> List.length el > 1)
|> Seq.length
List computations:
// https://docs.microsoft.com/en-us/dotnet/fsharp/tour
let daysList =
[ for month in 1 .. 12 do
for day in 1 .. System.DateTime.DaysInMonth(2017, month) do
yield System.DateTime(2017, month, day) ]
Active patterns:
// https://fsharpforfunandprofit.com/posts/convenience-active-patterns/
open System.Text.RegularExpressions
let (|FirstRegexGroup|_|) pattern input =
let m = Regex.Match(input,pattern)
if (m.Success) then Some m.Groups.[1].Value else None
// create a function to call the pattern
let testRegex str =
match str with
| FirstRegexGroup "http://(.*?)/(.*)" host ->
printfn "The value is a url and the host is %s" host
| FirstRegexGroup ".*?@(.*)" host ->
printfn "The value is an email and the host is %s" host
| _ -> printfn "The value '%s' is something else" str
// test
testRegex "http://google.com/test"
testRegex "alice@hotmail.com"
I've just found it nice and smooth to use. It's not hard to get from thought to code and have it work as intended. After a while learning all about building graphs in Rust, it's kinda nice to remember what that's like!
Some things I don't like:
- Compiler errors, but I'm spoiled with Rust/Reason
- Having to use the CLI to add things to solutions and references to packages and things. This is my own lack of familiarity with the ecosystem though.
That's really it, I like everything else a lot. It's the most fun I've had with an ML language so far, at least.
Try you some F#, today!
As an aside, does anyone have any experience using Clojure CLR? Seems not too popular, but a good idea in general.
This post isn't really about AoC, but here's an "obligatory" repo link if you'd like to play around with it.
Top comments (24)
Great post. :)
I assume you are using VS Code with Ionide plugin? If you open a folder with an F# project (
.fsproj
) file, it will add an icon to the left that looks like this.When you click that, it shows the F# Project Explorer window. You can right-click on the project and Add File. That should create the file and also add it to the
.fsproj
automatically. The Project Explorer is still a little rough around the edges. But helpful for basic usage.On Windows, I've been using Visual Studio 2017. It has a robust Solution Explorer that feels like normally managing files in an editor, but it automatically takes care of the
.fsproj
stuff. So no need to add them through CLI. However, VS does not have a built-in terminal window, so I use the MS-sponsored Whack Whack Terminal plugin for that. (to run npm/etc. commands from within VS for Fable projects.)Thanks so much for the advice!
I was using Ionide but had a rough go from the start and immediately fell back to the
dotnet
command. I'll give it another go, but I've also been meaning to start learning a bit about VS, too - might be a solid excuse. That plugin will definitely help it feel a little more comfortable. I've had an Elm project in the back of my head for a while - no reason it couldn't be Elmish!MVU style is amazing. Go for it either way! Elm is better for learning to write pure functions TBH. Fable-Elmish is structurally and syntactically very similar while being far less restrictive. For the latter, I do recommend keeping side effects out of init/update if you want to keep the refactor/test benefits from having pure functions. (Cuz F# does not stop you from putting side effects in those functions.)
Interesting - I do love compiler-enforced discipline, but it's always nice to have an escape hatch - and F# seems like a more broadly applicable language if I'm going to invest some time in one or the other. I'll have to think about it.
Yeah, I vote for F#. But wanted to give a props to Elm for teaching me the value of pure functions. I came from C# to F# and still fell back to a lot of mutation-based/imperative solutions for a while. Elm forced me do pure functions (along with MVU, which structures most of the code to be written in pure functions) and experience their benefits. But if you understand that pure (aka referentially transparent, aka deterministic) functions produce output based only on their inputs. And if you understand the value of using them. Then it is pretty easy to understand whether you have written a pure function or not in F#, even without a compiler warning. Plus F# is great for back-end work too. (Kestrel on .NET Core is known to be a pretty fast web server.)
Awesome. I think Haskell drilled the concept in pretty well but I still always sort of miss the rigidity whenever it's gone. I'm heavily leaning F# now too - I'll have to look in to Kestrel!
Kestrel is the built-in .NET Core web server. It isn't functional or anything, but it is very fast -- look for aspcore. Giraffe is a F#/functional lib on top of Kestrel. I also wrote my own for API routing only.
Cool! Definitely looking forward to digging deeper in to this, thank you.
Following up ...Visual Studio proper was so the way to go
If you want a better performance, easier JS interop (than Fable) you have to use OCaml. F# was "OCaml for .NET" before, and it's still interoptable with OCaml. They classified OCaml, for example
;;
is optional;
is can be replaced with\n
(Newline), foreach loop addedfor ... in ...
so on. You can still use the OCaml syntax, using verbose syntax (It's still part of F# Grammar and Parser, it's not a new language). In my opinion, learning OCaml before learning F# will be more useful for you.I did learn OCaml before I learned F# - and strongly prefer the latter. Thanks for the tip, though!
I agree, JS interop via BuckleScript is really nice, but everything else was harder to use than the equivalent in F#
Ah, I just remembered it now. There is OCaml preprocessor or macro language named camlp. That makes you easier to code OCaml, makes you have more features like F# have. For example for each loop
Nice post! Every language really should have Pipes. They are super! I have only used them in Elixir, but man I loved it.
Lovely post and very inspiring. Arrrgh I don't need another language that I now want to try.
Neither do I, Mark. Neither do I :(
A bit of experience using ClojureCLR several years ago. I had mostly .Net experience at the time and wanted to explore so tried it out. I created some WinForms and Console apps, successfully passing stuff to and from C# code. I can't say there were any glaring problems with it, but eventually it was obvious that .Net was a 2nd class platform. Just, tooling wasn't there, so I just moved to the Java ecosystem instead. F# is a lot tighter with .Net of course. (Though I'm surprised nobody ever did a JVM port!) I ended up settling on F# as my go-to: after just not being able to refactor in Clojure due to lack of type safety, I did some complete rewrites of fairly large Compojure apps to Suave, and never looked back.
That makes a lot of sense - I have a feeling spending more time with F# is the answer for me as well
Re: compiler errors - meaning the quality of the errors?
On another note, I see you're in Boston. Do you have any interest in helping to revive the Boston F# Meetup?
Hey! Gosh, I'm so sorry I missed this.
Not necessarily the errors themselves, but the error messages. Those two compilers are extremely helpful, and will often nudge you in the right direction, which as a total beginner is extremely useful. The F# compiler is a little more matter of fact about what's wrong, like most compilers. I think as I get more comfortable I won't find it as opaque, and it's not a huge drag on the experience.
I absolutely am interested - send me a message!
Good stuff.
It was (I believe) the first .net language to get async support. We kind of take async/await for granted now but F# was a game changer for me.
The units of measure stuff is also really awesome! I don't think I've seen anything else quite like it baked-in, only in libraries.
I'm looking into Rust but f# looks awesome to learn.
Those are my two favorites, I think! Very different, both worthwhile.
Really happy to hear how easy it was to jump in. I think with .net core F# should be much more approachable than in the past!