I have a function to calculate the cumulated sum of a sequence.
let cumsum<'T> = Seq.scan (+) 0 >> Seq.skip 1 >> Seq.toArray
Though it looks generic, the integer 0 makes it non-generic, and thus I cannot call the function with a sequence of floats.
Is there a generic zero that can replace my hardcoded 0, or maybe a different way of making the function generic.
You can use the GenericZero primitive but you need to make your function inline and make it explicitly a function (right now your function is written in point-free style) since in principle values cannot be made inline.
let inline cumsum s =
s |> Seq.scan (+) LanguagePrimitives.GenericZero |> Seq.skip 1 |> Seq.toArray
Note that by removing the Type parameter 'T the static member constraints are inferred automatically by the compiler:
val inline cumsum :
s:seq< ^a> -> ^b []
when ( ^b or ^a) : (static member ( + ) : ^b * ^a -> ^b) and
^b : (static member get_Zero : -> ^b)
LanguagePrimitives.GenericZero
:)
Related
Given a generic interface declaration like
type IFoo<'T,'MT> =
abstract Return : 'T -> 'MT
abstract Bind : 'MT * ('T -> 'MT) -> 'MT
it's actually possible to use object expressions as computation builder expressions, which could provide for an interesting approach to the division between encapsulation and execution logic of monadic workflows.
let inline addOption mx my =
{ new IFoo<_,_> with
member __.Return x = Some x
member __.Bind(ma, f) = Option.bind f ma }
{ let! x = mx
let! y = my
return x + y }
// val inline addOption :
// mx: ^a option -> my: ^a option -> ^a option
// when ^a : (static member ( + ) : ^a * ^a -> ^a)
addOption (Some 1) (Some 2)
// val it : int option = Some 3
addOption None (Some 2)
// val it : int option = None
The compiler checks on the type of the expression if the expected methods are present. But it is only halfway there; because for real monads, I would need to get the method signature abstract Bind : 'MT * ('T -> 'MU) -> 'MU honoured, a projection to a different non-encapsulated type. Why can't this be done?
In a project I'm experimenting with, I'm constantly multiplying sequence elements. I wanted to create an operator like this:
let (.*) (a:seq<'T>) (b:seq<'T>) = Seq.map2 (*) a b
However, in FSI it returns:
val ( .* ) : a:seq<int> -> b:seq<int> -> seq<int>
And the following fails:
seq [1.0;2.0] .* seq [3.0;4.0];;
-----^^^
stdin(16,6): error FS0001: This expression was expected to have type
int
but here has type
float
How can I make the operator work on a generic seq<'T> (provided 'T supports multiplication)?
You need to add the inline keyword to your operator declaration so that the correct overload can be resolved at compile time:
let inline (.*) (a:seq<'T>) (b:seq<'T>) = Seq.map2 (*) a b
val inline ( .* ) : a:seq< ^T> -> b:seq< ^T> -> seq< ^a> when ^T : (static member ( * ) : ^T * ^T -> ^a)
I am trying to reinvent List.fold, List.reduce, and List.sum using pointfree inline functions for the sake of learning. Here is what I had:
let flip f y x = f x y
let rec fold folder seed = function
| [] -> seed
| h :: t -> fold folder (folder seed h) t
let inline reduce< ^a when ^a : (static member Zero : ^a) > :
(^a -> ^a -> ^a) -> ^a list -> ^a =
flip fold LanguagePrimitives.GenericZero
let inline sum< ^a when
^a : (static member (+) : ^a * ^a -> ^a) and
^a : (static member Zero : ^a)> : ^a list -> ^a =
reduce (+)
// ^ Compile error here
I know such verbose type constraints are not frequently used in F# but I am trying to learn so please bear with me.
I got a very cryptic compile error which I do not understand:
Type mismatch. Expecting a
'a -> 'a -> 'a
but given a
'a -> 'b -> 'c
A type parameter is missing a constraint 'when ( ^a or ^?269978) : (static member ( + ) : ^a * ^?269978 -> ^?269979)'
A type parameter is missing a constraint 'when ( ^a or ^?269978) : (static member ( + ) : ^a * ^?269978 -> ^?269979)'
val ( + ) : x:'T1 -> y:'T2 -> 'T3 (requires member ( + ))
Full name: Microsoft.FSharp.Core.Operators.( + )
Overloaded addition operator
x: The first parameter.
y: The second parameter.
What should I add to satisfy the compiler?
I don't know a way of doing exactly as you want, because there's no way I know of to invoke a statically constrained static member in pointfree style. Note that you are not invoking (+) of ^a and the compiler does not know how to figure that out... if you are happy using a inner helper function, you can do:
let inline sum< ^a
when ^a : (static member (+) : ^a -> ^a -> ^a) and
^a : (static member Zero : ^a) > : ^a list -> ^a =
let add x y = (^a : (static member (+) : ^a -> ^a -> ^a) (x, y))
reduce add
Note the warning
warning FS0077: Member constraints with the name 'op_Addition' are given special status by the F# compiler as certain .NET types are implicitly augmented with this member. This may result in runtime failures if you attempt to invoke the member constraint from your own code.
and indeed:
sum [1; 2; 3] leads to
System.NotSupportedException: Specified method is not supported.
at FSI_0005.it#15-1.Invoke(Int32 x, Int32 y) in ...
But if you change sum to:
let inline sum list =
reduce (+) list
sum [1; 2; 3] // 6
My unhelpful answer would be: don't do that. Inline definitions are only intended to be syntactic functions, so you're not supposed to define them in a point-free way. For instance, compare the results of trying to evaluate the following three lines:
let inline identity = id
let inline identity : ^a -> ^a = id
let inline identity< ^a> : ^a -> ^a = id
Only the final one compiles, (technically this is because it's a "type function", which is basically a function that's passed an invisible unit value behind the scenes to allow it to be treated as a generic value).
However, if you insist on doing things this way, then one way to "fix" it is to make (+) less generic; by default the arguments and return type can be of different types and the constraints introduced by this flexibility are part of what's confusing the compiler. Here's one fix:
let inline reduce< ^a, 'b when ^a : (static member Zero : ^a)> : ( ^a -> 'b -> ^a) -> ('b list -> ^a) =
flip fold LanguagePrimitives.GenericZero
let inline (+) (x:^a) (y:^a) = x + y
let inline sum< ^a
when ^a : (static member ( + ) : ^a * ^a -> ^a)
and ^a : (static member Zero : ^a)> : ^a list -> ^a =
reduce (+)
Like Vandroiy, the given reduce function doesn't compile for me. I think what you wanted for your reduce function is actually:
let inline reduce< ^a when ^a : (static member Zero : ^a) > : ((^a -> ^a -> ^a) -> ^a list -> ^a) =
flip fold LanguagePrimitives.GenericZero
Doing that I was able to replicate your given error.
However, when I changed the sum function to compile by using a simple test function:
let inline sum< ^a when
^a : (static member Zero : ^a) and
^a : (static member (+) : (^a -> ^a -> ^a)) > : ^a list -> ^a =
reduce (fun i j -> i)
and trying to call sum [1..10] results in The type 'int' does not support the operator 'get_op_Addition'. So I'm not sure this methodology will even work.
EDIT: Thanks for the hints of CaringDev, I fixed the get_op_Addition error. This seems to work. Though as to why, I'm pretty baffled since 'b is just ^a in disguise...
let inline sum< ^a when
^a : (static member Zero : ^a) and
^a : (static member (+) : ^a -> ^a -> ^a) > : ^a list -> ^a =
let inline add (x:'b) (y:'b) = (+) x y
reduce add
Please explain the magic behind drawShape function. 1) Why it works at all -- I mean how it calls the Draw member, 2) why it needs to be inline?
type Triangle() =
member x.Draw() = printfn "Drawing triangle"
type Rectangle() =
member x.Draw() = printfn "Drawing rectangle"
let inline drawShape (shape : ^a) =
(^a : (member Draw : unit->unit) shape)
let triangle = Triangle()
let rect = Rectangle()
drawShape triangle
drawShape rect
And the next issue is -- is it possible to write drawShape function using parameter type annotation like below? I found that it has exactly the same signature as the first one, but I'm unable to complete the body.
let inline drawShape2 (shape : ^a when ^a : (member Draw : unit->unit)) =
...
Thanks in advance.
This Voodoo-looking syntax is called "statically resolved type parameter". The idea is to ask the compiler to check that the type passed as generic argument has certain members on it (in your example - Draw).
Since CLR does not support such checks, they have to be done at compile time, which the F# compiler is happy to do for you, but it also comes with a price: because there is no CLR support, there is no way to compile such function to IL, which means that it has to be "duplicated" every time it's used with a new generic argument (this technique is also sometimes known as "monomorphisation"), and that's what the inline keyword is for.
As for the calling syntax: for some reason, just declaring the constraint on the parameter itself doesn't cut it. You need to declare it every time you actually reference the member:
// Error: "x" is unknown
let inline f (a: ^a when ^a: (member x: unit -> string)) = a.x()
// Compiles fine
let inline f a = (^a: (member x: unit -> string)( a ))
// Have to jump through the same hoop for every call
let inline f (a: ^a) (b: ^a) =
let x = (^a: (member x: unit -> string)( a ))
let y = (^a: (member x: unit -> string)( b ))
x+y
// But can wrap it up if it becomes too messy
let inline f (a: ^a) (b: ^a) =
let callX t = (^a: (member x: unit -> string) t)
(callX a) + (callX b)
// This constraint also implicitly carries over to anybody calling your function:
> let inline g x y = (f x y) + (f y x)
val inline g : x: ^a -> y: ^a -> string when ^a : (member x : ^a -> string)
// But only if those functions are also inline:
> let g x y = (f x y) + (f y x)
Script.fsx(49,14): error FS0332: Could not resolve the ambiguity inherent in the use of the operator 'x' at or near this program point. Consider using type annotations to resolve the ambiguity.
In C# I can express this:
var xe = XElement.Parse("<foo></foo>");
var maybe = (bool?)xe.Element("bar");
How can this be expressed in F#?
Edit: I did find this helper function
let inline conv (x : ^a) : ^b = ((^a or ^b) : (static member op_Explicit : ^a -> ^b) (x))
Unfortunately, XLinq relies heavily on implicit and explicit conversions, which makes things a bit difficult.
You can make a routine to convert from an XElement to a bool option:
let elementToBool e =
match e with
| null -> None
| e -> Some(XElement.op_Explicit e : bool)
With this, you can write:
let xe = XElement.Parse("<foo><baz>true</baz></foo>")
let bar = xe.Element (XName.op_Implicit "bar") |> elementToBool
let baz = xe.Element (XName.op_Implicit "baz") |> elementToBool
In F# Interactive, this will convert to:
val bar : bool option = None
val baz : bool option = Some true
Note that you can use the helper function you found, though you'd also need one for the op_Implicit calls as well.
Using the converter functions this becomes a bit cleaner. I've adapted the code above to use (slightly modified versions of) your converter helper routine:
let inline convi (x : ^a) : ^b = ((^a or ^b) : (static member op_Implicit : ^a -> ^b) x)
let inline conve (x : ^a) : ^b = ((^a or ^b) : (static member op_Explicit : ^a -> ^b) x)
let xe = XElement.Parse("<foo><baz>true</baz></foo>")
let elementToBool e =
match e with
| null -> None
| e -> Some(conve e : bool)
let baz = "baz" |> convi |> xe.Element |> elementToBool
let bar = "bar" |> convi |> xe.Element |> elementToBool
There's now better support for implicit conversions in F# 6
https://learn.microsoft.com/en-us/dotnet/fsharp/whats-new/fsharp-6#first-class-support-for-net-style-implicit-conversions