Envoyé par
Vesa Karvonen
I too have been using F# for a few months. Before doing any programming
in F# I've programmed in both SML and (somewhat less in) O'Caml.
> many of the weaknesses in F# come from ML
Depends on which weaknesses you are referring to and what do you mean by
ML. Personally, I think that F# is an ML wannabee language that has been
intentionally bastardized for both legacy .NET and legacy O'Caml
interoperability for obvious reasons.
> The type checker in F# is powerful, but unpredictable.
I'm not sure whether I would call F#'s type checker powerful. I find F#'s
type system overall less expressive than O'Caml's or Alice ML's, for
example (although the type systems are likely to be technically
incomparable). But, particularly compared to SML, I'd say that F#'s type
system feels very fragile and rough. Calling it unpredictable would be a
misnomer, because it is quite deterministic and after a while you learn
the rough spots.
There are many idioms that work in both SML and O'Caml (and,
unsurprisingly, even in Haskell due to the H-M heritage), but fail
spectacularly in F# due to the fragility of the type system. For example,
in both SML and O'Caml, one can define the auxiliary function (here in SML
syntax)
fun undefined _ = raise Failure "undefined"
with the spec
val undefined : 'a -> 'b
which is quite useful, among other things, as a dummy value for functions
that you've specified in a module signature, but haven't yet implemented
in the corresponding module (again in SML syntax):
signature FOO = sig
val bar : int -> int
end
structure Foo :> FOO = struct
val bar = undefined
(* Fine, as 'a -> 'b can be instantiated to int -> int. *)
end
Unfortunately, in F#, this doesn't type check.
Another thing I took note of recently, is that F#'s type system is rather
unprincipal, so to say. Suppose you have a variable, ls, which is a list
of lists. One might assume that one could then write
List.map (fun l -> l.Length) ls
But this doesn't type check (without further annotations). On the other
hand, the following does
ls |> List.map (fun l -> l.length)
This just basically follows from the fact that F#, with those pesky
nominal .NET object types, has no notion of principal types or typings to
speak of.
These are just two isolated examples that I could recall immediately.
While using F# for a few months, I've run into *many* rough corners like
these. Compared to SML, it feels like I have to constantly spoon feed
F#'s crybaby type checker.
> The tuple type is treated specially in many cases, and this also leads
> to surprise - inserting or removing a pair of brackets can effect the
> type checker.
This collection of deficiencies is inherited from both O'Caml and .NET.
First of all, O'Caml's syntax allows one to omit parentheses from tuples
(in both patterns and expressions) in a number of places, which can be
quite confusing. In addition, what looks like a tuple type in a data
constructor isn't actually a tuple type, even if it looks like one.
(Neither of these warts exist in SML.) The same goes for multiple
argument .NET functions.
> In most cases, this state became problematic later.
Details please. Where and how did you use state and when and how did it
become problematic?
> All of the standard .NET libraries work with arrays, but for a
> functional program the list is a more natural type. F# provides both,
> and it was never clear which I should use where, leading to lots of
> conversions.
I think that answer to that is to use sequences (IEnumerable) rather than
arrays or lists in interfaces intended for interoperable code. Both
arrays and lists can be trivially converted to sequences. Inside the
abstraction boundary, you can then use whatever sequence type (arrays,
lists, both, or something else) that has the desired properties.
Partager