The big reason to not use F# vs OCaml tends to come down to the lack of functors... but it looks like your current codebase might not be using them much.
The big reason to not use F# vs OCaml tends to come down to the lack of functors... but it looks like your current codebase might not be using them much.
One of the biggest problems they mention is with postgres. F# has a postgres problem as well.
Your options in f# for postgres usage include the following high-level categories:
• Dapper/RepoDb/Other community libs
• Typeprovider based like Fsharp.Data.Npgsql
• EF core (where you build the fsharp design-time support yourself)
• Using C# in another project as a database interaction abstraction
I've had to mix and match for the projects I've worked on because I've had trouble finding a single library where:
1) Joins are supported in a sane way
2) Batch inserts work
3) Interacting with native features (views, functions, SET variables) works
4) It's still in fsharp
It's not bad, but when I work on $DAYJOB projects in Typescript/etc where database access is smooth, I wonder how long it might take to rewrite for the database heavy projects.
Also should mention that I'm very thankful for the libraries that _do_ exist, and the effort put into making them as feature as they are. It's just one of the areas that I don't think is a strength of F# right now.
Definitely a personal take here, but since I've worked for a while with postgres, a "high-level, production web stack" for me is something that has an established, good DX way to work with this popular database.
Managing ADO.NET connections, working around the type systems and writing a lot of mapping/connecting code are all things that don't solve my business problem.
While not prolific, I've written a handful of production web applications in F# (Suave, Giraffe, Saturn, and Razor Pages) as well as other stacks, and have found a hybrid approach that works for my concerns, but I've found a comprehensive solution not as simple as other ecosystems.
I see questions frequently about data access and serialization/validation in F# web apps, which I had specific sections about. Hopefully I'll have some time in the near future to continue the work.
In the mean time feel free to DM me any questions. There's nothing too fancy about the code. If you looked at other production code, and swapped out what your best guess for the F# translation would be, you're probably most of the way there.
Same thing exists from MS SQL https://github.com/Zaid-Ajaj/DustyTables , also great.
Used both in prod. No regrets and wouldn't wan't something else. Also involves zero magic - just raw SQL and a nice functional API.
I believe there is some work with F# analyzers to provide compile-time checking of raw SQL, although I haven't tried it yet.
Yep, never tried it either. https://github.com/Zaid-Ajaj/Npgsql.FSharp.Analyzer
They'll not going to pick F# because they're in competition with Azure.
They don't want ocaml because of marketability. It's technically the right choice and direction but the learning curve for developers is turning them off the platform as a whole.
Yes, there are ways to write it in a .NET-y way in F#, but then it requires a "complete" rewrite and F# gives no real benefits with "being similar".
In contrast, after that I migrated to Rust and I am delighted. I wrote about it here: https://news.ycombinator.com/item?id=24893285
Of course, this is also a full-rewrite without using the same idioms as in OCaml (mostly because I mutate objects directly when needed) but I still think it's much better than F# as the language (Rust) is miles ahead in the practicality domain. Indeed, it's much better than anything I've seen so far. And still so very much "functional" and "elegant". I just miss the transparent currying from OCaml.
That's a bit overstatement. You can call .Net code from F#, just like you can call Java from Clojure or Python/Rust/C from OCaml.
It just would be foreign and unidiomatic (hello, null pointers, methods, OOP).
And you can call C/Python/Rust from OCaml, we made quite a lot of wrappers for Gstreamer and some Rust libs in OCaml and it was quite painless.
So it's a bit of simplification, you can call foreign code in OCaml, it's not that much harder than F# considering there are higher level FFIs for C, Rust and Python available. Author could just use these as well.
This is because F# and C# share:
* build tools
* package management
* garbage collector
* base types (string, int, etc).
The F# type-checker understands C# types, so you know that you are consuming the C# API correctly (at least at the type-level). F# can be be used as a new syntax for C#, if you wanted to go that way.
What do you mean by that? F# has support for functors, or are you referring to something else? https://fsprojects.github.io/FSharpPlus/applicative-functors...
The best I could describe the general concept is that a functor allows you to take something of some type and maps it to another type via some unspecified function.
Simple example: in an OOP language you might have a Map<K, V> class for any type K, V where K must be an instance of an Ordered interface so that the map implementation can do efficient lookup.
In OCaml, you have a Map.Make(Ord : Ordered) functor which explicitly takes a module Ord as its parameter. The Ord module implements the key data type, which must support the Ordered interface. When you (statically) apply the functor, you get back a new module which specializes a map data type to work for the specific key type. E.g.,
module StringMap = Map.Make(String)
The String module implements the Ordered interface, so it can be used as the argument here. Now you can create and manipulate maps of strings to any values.You might be wondering, what's the payoff? It's very similar to the payoff for interfaces--code abstraction. It's just that it's all statically resolved and highly efficient.
It depends on what you mean by "generics". Functors are generics, but very powerful ones, akin to packages in Ada.
They are way more generic than what parametric polymorphism allows you to do in F#, they support Higher Kinded Types, module can have many type variables within it etc etc.
I said 'traditionally' above because OCaml actually has dynamic dispatch now; it has a powerful and feature-complete OOP implementation, and a way to pass around modules at runtime as first-class objects. And in fact people take advantage of these capabilities to build more 'modern' APIs. But I would argue that the functor approach is one of, if not the best, overall.
Instead, they're roughly "functions from modules to modules": https://dev.realworldocaml.org/functors.html. Functors allow you to replicate much of what classes/objects in an OO language offer, but with static instead of dynamic dispatch.
Real experts should let me know if I am mistaken: I don’t think OCaml and Haskell have strictly the same type system, but they are both similarly more expressive than F# because of this specific type -> type polymorphism that in particular lets you do type-checked monads, etc.
In OCaml, functors map between an abstract module and some concrete one. And in Haskell functors are structures that can have a function that maps over their elements. In math a functor is a map between categories. So you can see how all three kinda make sense as functors, but also are different in exactly what they are.
https://caml.inria.fr/pub/docs/manual-ocaml/libref/Hashtbl.M...
I think in OCaml, "functor" basically just means "like a function but for modules". They might technically be categorical functors, but it seems quite different to the meaning in Haskell where they are plainly categorical functors (modulo Hask technically not being a category).
A function S -> T maps a S(ource) type to a T(arget) type, like FunctorOf (-S>) (-T>) does between the source category (-S>) and target category (-T>)
type Functor :: forall (s :: Type) (t :: Type). (s -> t) -> Constraint
class (Category (Src f), Category (Tgt f)) => Functor (f :: s -> t) where
type Src (f :: s -> t) :: Cat s
type Tgt (f :: s -> t) :: Cat t
fmap :: Src f a1 a2 -> Tgt f (f a1) (f a2)
type FunctorOf :: forall (s :: Type) (t :: Type). Cat s -> Cat t -> (s -> t) -> Constraint
type FunctorOf src tgt f = (Functor f, Src f ~ src, Tgt f ~ tgt)
The usual endofunctor type EndofunctorOf :: forall (ob :: Type). Cat ob -> (ob -> ob) -> Constraint
type EndofunctorOf @ob cat f = FuntorOf @ob @ob cat cat f
we have in Haskell can be defined as FunctorOf @Type @Type (->) (->), or type OldFunctor :: (Type -> Type) -> Constraint
type OldFunctor f = EndofunctorOf @Type (->) f type FunctorOf :: forall (s :: Type) (t :: Type). Cat s -> Cat t -> Constraint
class (Functor f, Src f ~ src, Tgt f ~ tgt) => FunctorOf src tgt f
instance (Functor f, Src f ~ src, Tgt f ~ tgt) => FunctorOf src tgt f
The other definitions can be eta reduced type EndofunctorOf cat = FunctorOf cat cat
type OldFunctor = EndofunctorOf (->)(standard caveat that benchmarks suck)
[1] https://benchmarksgame-team.pages.debian.net/benchmarksgame/...