Are there any things that I should keep in mind when studying F #, after having studied the Scheme first? Any differences in methodologies, gotchas or other things that might cause me problems?
Static typing is the main difference between Schema and F #. This facilitates a style called generic programming, where a type system is used to encode constraints on functions and data, so that the compiler proves these aspects of the program correctly at compile time, and any breach of constraints immediately gets caught.
For example, a sequence of one or more elements of the same type can be transmitted by a value of the following type:
type list1<'a> = List1 of 'a * 'a list let xs = List1(1, []) let ys = List1(2, [3; 4])
Now the compiler guarantees that any attempt to use an empty one of these sequences will be detected at compile time as an error.
Now the reduce function does not make sense in an empty sequence, so the built-in implementation for barfs lists at runtime with the exception if it encounters an empty sequence:
> List.reduce (+) [];; System.ArgumentException: The input list was empty. Parameter name: list at Microsoft.FSharp.Collections.ListModule.Reduce[T](FSharpFunc`2 reduction, FSharpList`1 list) at <StartupCode$FSI_0271> .$FSI_0271.main@ () Stopped due to error
With our new sequence of one or more elements, we can now write a reduce function that never lags at runtime with an exception because its input is guaranteed by a non-empty type system:
let rec reduce f = function | List1(x, []) -> x | List1(x0, x1::xs) -> f x0 (reduce f (List1(x1, xs)))
This is a great way to improve software reliability by eliminating sources of runtime errors, and this is something that dynamically typed languages like Scheme cannot even start.
Jon harrop
source share