Higher order functions with generic argument in F# - f#

RE: What is the best way to pass generic function that resolves to multiple types
Please read the referenced link before going further below
I am trying to extend the concept and pass a generic function that takes 2 parameters and does something with them.
The static approach works, however the interface based one causes a compile error (see the code lines marked with //error):
The declared type parameter '?' cannot be used here since the type parameter cannot be resolved at compile time.
Does anyone know how to fix it?
module MyModule
type T = Content of int
with
static member (+) ((Content i1), (Content i2)) = Content (i1 + i2)
static member (*) ((Content i1), (Content i2)) = Content (i1 * i2)
type W = { Content: int }
with
static member (+) ({Content = i1}, {Content = i2}) = { Content = i1 + i2 }
static member (*) ({Content = i1}, {Content = i2}) = { Content = i1 * i2 }
type Sum = Sum with static member inline ($) (Sum, (x, y)) = x + y
type Mul = Mul with static member inline ($) (Mul, (x, y)) = x * y
let inline f1 (la: 'a list) (lb: 'b list) reducer =
let a = la |> List.reduce (fun x y -> reducer $ (x, y))
let b = lb |> List.reduce (fun x y -> reducer $ (x, y))
(a, b)
type I = abstract member Reduce<'a> : 'a -> 'a -> 'a
let f2 (la: 'a list) (lb: 'b list) (reducer: I) =
let a = la |> List.reduce reducer.Reduce
let b = lb |> List.reduce reducer.Reduce
(a, b)
let main ()=
let lt = [Content 2; Content 4]
let lw = [{ Content = 2 }; { Content = 4 }]
let _ = f1 lt lw Sum
let _ = f1 lt lw Mul
let _ = f2 lt lw { new I with member __.Reduce x y = x + y} //error
let _ = f2 lt lw { new I with member __.Reduce x y = x * y} //error
0

The problem with your attempt is that you can't use operators + or * on parameters x and y, because it's not known that their type 'a has those operators defined.
To answer your further question in comments about how to achieve it anyway - if you want to use multiplication and addition on any type 'a that the caller chooses, you have to specify that. For an interface method, the only way to do this is by constraining the type parameter 'a, and the only two kinds of constraints that .NET runtime supports are "has a parameterless constructor" and "implements a given interface or inherits from a given class".
The latter one would be useful in your case: make both types implement the interface and then constrain type parameter 'a to implement that interface:
type IArithmetic<'a> =
abstract member add : 'a -> 'a
abstract member mult : 'a -> 'a
type T = Content of int
with
interface IArithmetic<T> with
member this.add (Content y) = let (Content x) = this in Content (x + y)
member this.mult (Content y) = let (Content x) = this in Content (x * y)
type W = { Content: int }
with
interface IArithmetic<W> with
member this.add y = { Content = this.Content + y.Content }
member this.mult y = { Content = this.Content * y.Content }
type I = abstract member Reduce<'a when 'a :> IArithmetic<'a>> : 'a -> 'a -> 'a
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
// the constraint right here
...
let _ = f2 lt lw { new I with member __.Reduce x y = x.add y }
let _ = f2 lt lw { new I with member __.Reduce x y = x.mult y }
Is this a bit awkward? I guess so, but you're kind of doing the same thing for the SRTP version, so why not?
The core idea is: if you want your Reduce method to work not with just any type, but only with types that can do certain things, you have to specify what those things are. In the SRTP case you're doing that by defining the (+) and (*) operators. In the interface case you're doing that by implementing the interface.
Q: But can I make the interface somehow pick up the (+) and (*) operators?
A: In general, no. The .NET runtime just doesn't support the kind of constraints like "any type that has a method with certain signature". This means that such constraints can't be compiled down to IL, which means they can't be used in an interface implementation.
And this is the price you pay for using SRTPs: all those inline functions - they don't get compiled to IL, they always get expanded (inserted, substituted) at use sites. For small, simple functions, this is no big deal. But if your whole program is like that, you might see some unexpected compiled code bloat, potentially translating to slower startup time etc.
Having said all that, I must note that the code you're showing is toy POC kind of code, not intended to solve any real, practical problem. And as such, most musings on it are in danger of being completely useless.
If you have an actual problem in mind, perhaps try sharing it, and somebody would suggest the best solution for that specific case.
In particular, I have a nagging feeling that you might not actually need higher-rank functions (that's what it's called when a function doesn't lose genericity when passed as parameter).

Related

Type inference error in computation expression

type Identity<'T> = Identity of 'T
type IdentityBuilder() =
member __.Bind (Identity x) (k : 'a -> Identity<'b>) = k x
member __.Return x = Identity x
let identity = new IdentityBuilder()
let three = Identity 3
let four = Identity 4
let twelve =
identity.Bind three <| fun t ->
identity.Bind four <| fun f ->
identity.Return (t * f)
let twelve2 = identity {
let! t = three
let! f = four
return t * f
}
twelve does not introduce any problem, but twelve2 gives
FS0001: This expression was expected to have type
'Identity<'a>' but here has type
''b * 'c'
on the line let! t = three.
I thought twelve and twelve2 should be equivalent... Was I mistaken?
As noted in the comment by Szer, you need to use tupled parameters for the Computation Builder methods. However, it is often convenient to use the curried versions for pipelining, as in your example. Therefore, what I usually do is create a module that contains all the functions required for the Computation Builder in curried form, and then use them in the builder itself. That way I can use either the computation expression syntax or the pipelining syntax depending on the scenario.
In your case, that would look something like this:
type Identity<'T> = Identity of 'T
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Identity =
let bind (f: 'T -> Identity<'U>) (Identity x) = f x
let create x = Identity x
type IdentityBuilder() =
member __.Bind (x, f) = Identity.bind f x
member __.Return x = Identity.create x
let identity = new IdentityBuilder()
let three = Identity 3
let four = Identity 4
let twelve =
three |> Identity.bind (fun t ->
four |> Identity.bind (fun f ->
Identity.create (t * f)))
let twelve2 = identity {
let! t = three
let! f = four
return t * f
}
Another common practice is to define an operator >>= for the bind function so that you can streamline the syntax even more:
let (>>=) f x = Identity.bind x f
let twelve3 = three >>= (fun t -> four >>= (fun f -> Identity.create (t * f)))

Erroneous FS0064 warning? This construct causes code to be less generic than indicated by the type annotations

I am trying to make a class which implements a generic .NET IComparer interface for use with the various System.Collections.Generic collections. This IComparer will map the collection's values into an ordering key using an externally provided function.
An example application might be a SortedSet<City> which sorts by population, but where the population is dynamically retrieved from an external data source which is separately maintained and updated.
open System
open System.Collections.Generic
type ExternalComparer<'T>(compareBy: ('T -> 'U) when 'U :> IComparable) =
let compareBy = compareBy
interface IComparer<'T> with
member this.Compare(a, b) =
let x = compareBy a
let y = compareBy b
if x < y then -1 else if x > y then 1 else 0
The compiler issues the following warning on the if x < y expression, specifically on the x:
Warning FS0064 This construct causes code to be less generic than
indicated by the type annotations. The type variable 'U has been
constrained to be type 'IComparable'.
In fact, I intend the type variable 'U to be constrained to be the type 'IComparable,' and I tried to express that intent with the when 'U :> IComparable constraint in the definition of the compareBy function type.
Is this warning message erroneous, or am I doing something wrong?
I think your solution - just accept a function of type 'T -> IComparable is the right thing to do, even if it means that the users of your ExternalComparer might need to insert an upcast to make the code compile.
To explain why you were getting the error, your class has only one generic type parameter 'T and the 'U parameter is missing there - so the compiler constrained it to IComparable. If you wanted to fix that, you'd have to add another generic parameter:
type ExternalComparer<'T, 'U when 'U :> IComparable>(compareBy: ('T -> 'U)) =
let compareBy = compareBy
interface IComparer<'T> with
member this.Compare(a, b) =
let x = compareBy a :> IComparable
let y = compareBy b :> IComparable
if x < y then -1 else if x > y then 1 else 0
Additionally, I also had to add :> IComparable so that we convert the results from whatever 'U is to IComparable that can be compared. This adds another generic parameter, which is quite silly.
If you wanted to avoid that, you could use a static member that converts a 'T -> 'U function to
'T -> IComparable function before passing it to the constructor:
type ExternalComparer<'T>(compareBy: ('T -> IComparable)) =
let compareBy = compareBy
interface IComparer<'T> with
member this.Compare(a, b) =
let x = compareBy a
let y = compareBy b
if x < y then -1 else if x > y then 1 else 0
static member Create(compareBy : 'T -> #IComparable) =
ExternalComparer(fun v -> compareBy v :> IComparable)
Now you can use the Create method nicely:
ExternalComparer<string>(fun v -> int v :> _) // Requires upcast
ExternalComparer<string>.Create(fun v -> int v) // Works directly!
The problem is that you're defining the compareBy type to return a 'U, constrained to IComparable, when you could more clearly have defined it to return an IComparable directly, like this:
type ExternalComparer<'T>(compareBy: ('T -> IComparable)) =
Since you know 'U will always be an IComparable, the use of a generic type is incorrect.
It seems to me you could collapse a chunk of the code into an object expression:
let bigCity = {Name="NYC"; population = 1_000_000}
let smallCity = {Name="KC"; population = 1000}
let cityComparer compFunc = { new System.Collections.Generic.IComparer<'T> with
override __.Compare(c1, c2) =
let x = compFunc c1
let y = compFunc c2
if x < y then -1 else if x > y then 1 else 0
}
let comp = cityComparer (fun x -> x.population)
comp.Compare(smallCity,bigCity)
//val it : int = -1

How to implement the List Monad (computation expression) with a condition?

I'm trying to understand how to use F# computation expressions and it certainly puzzles me.
The following example makes some amount of sense to me.
type ListMonad() =
member o.Bind( (m:'a list), (f: 'a -> 'b list) ) = List.collect f m
member o.Return(x) = [x]
let list = ListMonad()
let test =
list {
let! x = [ 1 .. 10]
let! y = [2 .. 2 .. 20]
return (x,y)
}
My question is, how would you add a condition to this computation expression? Specifically, how would you alter it to return a list of elements only where the x value is strictly smaller than the y value? (Let's not filter it out afterward).
Since computation expressions can be parameterized, you might first think to try something like this:
let filterAndCollect (pred : 'a -> 'b -> bool) (f : 'a -> 'b list) (m : 'a list) =
let f' a = [ for b in f a do if pred a b then yield b ]
List.collect f' m
type FilteringListMonad(pred) =
member o.Bind( (m:'a list), (f: 'a -> 'b list) ) = filterAndCollect pred f m
member o.Return(x) = [x]
let filteredList = FilteringListMonad(fun x y -> x < y)
let test2 =
filteredList {
let! x = [1 .. 10]
let! y = [2 .. 2 .. 20]
return (x,y)
}
However, that fails with a type error on the (x,y) tuple:
This expression was expected to have type 'int' but here has type ''a * 'b'
There are also two compiler warnings: on the y of the x < y expression in the FilteringListMonad constructor, there's a warning:
This construct causes code to be less generic than indicated by the type annotations. The type variable 'a has been constrained to be type ''b'.
And on the number 1 in the let! x = [1 .. 10] expression, there's a warning:
This construct causes code to be less generic than indicated by the type annotations. The type variable 'b has been constrained to be type 'int'.
So between these two constraints, the return type of the computation expression (a 'b list) has been constrained to be int list, but your expression is returning a int * int list instead. After some thinking about the type constraints, you might conclude that there's no way this can work. But there's a way to make it work. The key is to realize that the 'b type that will be the output of your computation expression is, in this example, actually the tuple int * int, so you rewrite the predicate function to actually just take that 'b type, and then everything works:
let filterAndCollect (pred : 'b -> bool) (f : 'a -> 'b list) (m : 'a list) =
let f' a = [ for b in f a do if pred b then yield b ]
List.collect f' m
type FilteringListMonad(pred) =
member o.Bind( (m:'a list), (f: 'a -> 'b list) ) = filterAndCollect pred f m
member o.Return(x) = [x]
let filteredList = FilteringListMonad(fun (x:int,y:int) -> x < y)
let test2 =
filteredList {
let! x = [ 1 .. 10]
let! y = [2 .. 2 .. 20]
return (x,y)
}
Note that I also had to specify the types of the predicate function's inputs. Without that, F# was generalizing them to be "any type that implements System.IComparable, but I was passing in ints, which are value types and thus don't implement any interfaces. This led to the error
This expression was expected to have type 'System.IComparable' but here has type 'int'.
However, declaring both parameters to the predicate as int did the trick.
OP already accepted an answer, I will just provide a different approach which might be useful to help understand computation expressions in F#
One can extend computation expression with the useful ReturnFrom and Zero like so:
type ListMonad() =
member o.Bind (m, f) = List.collect f m
member o.Return x = [x]
member o.ReturnFrom l = l : _ list
member o.Zero () = []
let listM = ListMonad()
ReturnFrom allows us to return empty lists using return! [] as a result which enables filtering. Zero is a short-hand for this in that if the else branch isn't defined Zero is used.
This allows us to filter like so:
let test =
listM {
let! x = [ 1 .. 10]
let! y = [2 .. 2 .. 20]
if x % y = 0 then
return (x,y)
// By defining .Zero F# implicitly adds else branch if not defined
// else
// return! []
}
F# will expand the computation into something roughly like this:
let testEq =
[ 1 .. 10]
|> List.collect
(fun x ->
[2 .. 2 .. 20]
|> List.collect (fun y -> if x % y = 0 then [x,y] else [])
)

Storing multidimensional points in F#

I am currently porting some code from Java to F# that deals with multidimensional functions. It supports variable dimension, so in the original implementation each point is represented as an array of doubles. The critical function of the code is an optimisation routine, that basically generates a sequence of points based on some criteria, evaluates a given function at these points and looks for a maximum. This works for any dimension. The operations I need are:
check the dimension of a point
create a new point with the same dimension of a given point
set (in procedural or functional sense) a given coordinate of a point
In F# I could obviously also use arrays in the same way. I was wandering though if there is a better way. If the dimension was fixed in advance, the obvious choice would be to use tuples. Is it possible to use tuples in this dynamic setting though?
No, tuples will be fixed by dimension. Also note that .NET tuples are boxed. If you are operating on large collections of points with small dimension (such as arrays of 2d points), using structs may help.
If you really want to push the F#/.NET advantage over Java, have a look at generics. Writing code with generics allows to write code that works for any dimension, and use different representations for different dimensions (say structs for 1-3 dimensions, and vectors for larger dimensions):
let op<'T where 'T :> IVector> (x: 'T) =
...
This is only relevant though if you are willing to go a long way to get the absolutely best performance and generality. Most projects do not need that, stick with the simplest thing that works.
For the fun of it, here is an extended example of how to utilize generics and F# inlining:
open System.Numerics
type IVector<'T,'V> =
abstract member Item : int -> 'T with get
abstract member Length : int
abstract member Update : int * 'T -> 'V
let lift<'T,'V when 'V :> IVector<'T,'V>> f (v: 'V) : 'V =
if v.Length = 0 then v else
let mutable r = v.Update(0, f v.[0])
for i in 1 .. v.Length - 1 do
r <- r.Update(i, f v.[i])
r
let inline norm (v: IVector<_,_>) =
let sq i =
let x = v.[i]
x * x
Seq.sum (Seq.init v.Length sq)
let inline normalize (v: 'V) : 'V =
let n = norm v
lift (fun x -> x / n) v
[<Struct>]
type Vector2D<'T>(x: 'T, y: 'T) =
member this.X = x
member this.Y = y
interface IVector<'T,Vector2D<'T>> with
member this.Item
with get (i: int) =
match i with
| 0 -> x
| _ -> y
member this.Length = 2
member this.Update(i: int, v: 'T) =
match i with
| 0 -> Vector2D(v, y)
| _ -> Vector2D(x, v)
override this.ToString() =
System.String.Format("{0}, {1}", x, y)
[<Sealed>]
type Vector<'T>(x: 'T []) =
interface IVector<'T,Vector<'T>> with
member this.Item with get (i: int) = x.[i]
member this.Length = x.Length
member this.Update(i: int, v: 'T) =
let a = Array.copy x
a.[i] <- v
Vector(a)
override this.ToString() =
x
|> Seq.map (fun e -> e.ToString())
|> String.concat ", "
[<Struct>]
type C(c: Complex) =
member this.Complex = c
static member Zero = C(Complex(0., 0.))
static member ( + ) (a: C, b: C) = C(a.Complex + b.Complex)
static member ( * ) (a: C, b: C) = C(a.Complex * b.Complex)
static member ( / ) (a: C, b: C) = C(a.Complex / b.Complex)
override this.ToString() = string c
let v1 = Vector2D(10., 30.)
normalize v1
|> printfn "%O"
let v2 = Vector2D(C(Complex(1.25, 0.8)), C(Complex(0.5, -1.)))
normalize v2
|> printfn "%O"
let v3 = Vector([| 10.; 30.; 50.|])
normalize v3
|> printfn "%O"
Note that norm and normalize are fairly general, they cope with specialized 2D vectors and generalized N-dimensional vectors, and with different component types such as complex numbers (you can define your own). The use of generics and F# inlining ensure that while general, these algorithms perform well for the special cases, using compact representations. This is where F# and .NET generics shine compared to Java, where you are obliged to create specialized copies of your code to get decent performance.

F# Checked Arithmetics Scope

F# allows to use checked arithmetics by opening Checked module, which redefines standard operators to be checked operators, for example:
open Checked
let x = 1 + System.Int32.MaxValue // overflow
will result arithmetic overflow exception.
But what if I want to use checked arithmetics in some small scope, like C# allows with keyword checked:
int x = 1 + int.MaxValue; // ok
int y = checked { 1 + int.MaxValue }; // overflow
How can I control the scope of operators redefinition by opening Checked module or make it smaller as possible?
You can always define a separate operator, or use shadowing, or use parens to create an inner scope for temporary shadowing:
let f() =
// define a separate operator
let (+.) x y = Checked.(+) x y
try
let x = 1 +. System.Int32.MaxValue
printfn "ran ok"
with e ->
printfn "exception"
try
let x = 1 + System.Int32.MaxValue
printfn "ran ok"
with e ->
printfn "exception"
// shadow (+)
let (+) x y = Checked.(+) x y
try
let x = 1 + System.Int32.MaxValue
printfn "ran ok"
with e ->
printfn "exception"
// shadow it back again
let (+) x y = Operators.(+) x y
try
let x = 1 + System.Int32.MaxValue
printfn "ran ok"
with e ->
printfn "exception"
// use parens to create a scope
(
// shadow inside
let (+) x y = Checked.(+) x y
try
let x = 1 + System.Int32.MaxValue
printfn "ran ok"
with e ->
printfn "exception"
)
// shadowing scope expires
try
let x = 1 + System.Int32.MaxValue
printfn "ran ok"
with e ->
printfn "exception"
f()
// output:
// exception
// ran ok
// exception
// ran ok
// exception
// ran ok
Finally, see also the --checked+ compiler option:
http://msdn.microsoft.com/en-us/library/dd233171(VS.100).aspx
Here is a complicated (but maybe interesting) alternative. If you're writing something serious then you should probably use one of the Brians suggestions, but just out of curiosity, I was wondering if it was possible to write F# computation expression to do this. You can declare a type that represents int which should be used only with checked operations:
type CheckedInt = Ch of int with
static member (+) (Ch a, Ch b) = Checked.(+) a b
static member (*) (Ch a, Ch b) = Checked.(*) a b
static member (+) (Ch a, b) = Checked.(+) a b
static member (*) (Ch a, b) = Checked.(*) a b
Then you can define a computation expression builder (this isn't really a monad at all, because the types of operations are completely non-standard):
type CheckedBuilder() =
member x.Bind(v, f) = f (Ch v)
member x.Return(Ch v) = v
let checked = new CheckedBuilder()
When you call 'bind' it will automatically wrap the given integer value into an integer that should be used with checked operations, so the rest of the code will use checked + and * operators declared as members. You end up with something like this:
checked { let! a = 10000
let! b = a * 10000
let! c = b * 21
let! d = c + 47483648 // !
return d }
This throws an exception because it overflows on the marked line. If you change the number, it will return an int value (because the Return member unwraps the numeric value from the Checked type). This is a bit crazy technique :-) but I thought it may be interesting!
(Note checked is a keyword reserved for future use, so you may prefer choosing another name)

Resources