I'm looking for a shorter way (if there is one) to constraint a function. E.g.
let inline sincos (a:'T) =
let y = sin a
let x = cos a
y, x
For using this function, 'T will need to support both Sin and Cos static members.
I can constraint it to work on float32 with:
let sincosf = sincos : float32 -> float32 -> float32
or using some wildcard:
let sincosf = sincos : float32 -> _
My question is, would it be possible to add a type parameter to sincos so I just have to write:
let sincosf = sincos<float32>
Thanks in advance.
Indeed, let sincosf = sincos<float32> will give you a warning, but will work as is.
As I noted in the comments, it should be possible to explicitly provide a type parameter on the definition of sincos which would remove the warning. However, this also requires all of the inferred constraints on that parameter to be specified, which can be a bit ugly. In this case, the F# compiler unexpectedly rejects the definition anyway:
let inline sincos< ^t when ^t : (static member Sin : ^t -> ^t)
and ^t : (static member Cos : ^t -> ^t)> (a: ^t) =
let y = sin a
let x = cos a
y, x
I believe that this is a compiler bug.
Related
I am having a major issue handling units of measure in F#. I have handled them successfully so far, but I have only either been removing units to get back a float or casting a float to one with units. However, now I have a use case where I need to generically drop units off of a float and give them new units. At present, I see no way that this is possible in F#.
For example:
> let removeUnits (x: float<'Unit>) = float x;;
val removeUnits: x: float<'Unit> -> float
> let castUnits<[<Measure>] 'Unit> (x: float) = LanguagePrimitives.FloatWithMeasure<'Unit> x;;
val castUnits: x: float -> float<'Unit>
> let convertUnits<[<Measure>] 'NewUnit> (x: float<'OldUnit>) : float<'NewUnit> =
x |> removeUnits |> castUnits<'NewUnit>;;
let convertUnits<[<Measure>] 'NewUnit> (x: float<'OldUnit>) : float<'NewUnit> =
-------------------------------------------------^^^^^^^^
stdin(8,47): warning FS0064: This construct causes code to be less generic than indicated by the type annotations. The unit-of-measure variable 'OldUnit has been constrained to be measure '1'.
val convertUnits: x: float -> float<'NewUnit>
This almost seems like a bug to me. The types of the pipeline should be float<'OldUnit> -> float -> float<'NewUnit>, because those are the type signatures of the component functions. I'm not understanding why out of nowhere that 'OldUnit is being constrained to 1.
How can I do this properly? I want a function with signature val convertUnits: x: float<'OldUnit> -> float<'NewUnit>, where the function only changes the units and leaves the underlying float values alone.
Is this a bug in the compiler?
That's just because you explicitly gave your function only one type parameter - 'NewUnit, - but in its signature you're using two - 'OldUnit and 'NewUnit.
Give the function both parameters like this:
let convertUnits<[<Measure>] 'NewUnit, [<Measure>] 'OldUnit> (x: float<'OldUnit>) : float<'NewUnit> =
x |> removeUnits |> castUnits<'NewUnit>
Or, alternatively, let the compiler infer them by itself:
let convertUnits (x: float<'OldUnit>) : float<'NewUnit> =
x |> removeUnits |> castUnits<'NewUnit>
I'm looking to make a function that handles a seq of any numeric type of data (int, float, double), does a little computation on it via mapping, then does a summation of these calculated values. The problem I am running into is that Seq.sum (or really just '(+)' in general) causes the type parameters to be integer, or just give a flat out error. It seems there there should be a way to make this work by using type constraints but I can't seem to get it.
type ValueWithComputation<'v> = {Value: seq<'v>; Computation: 'v -> 'v}
let calculateAndCombine (x: ValueWithComputation<'v>) =
x.Value
|> Seq.map x.Computation
|> Seq.sum // sometimes gives error: "Could not resolve the ambiguity inherent in the use of operator '(+)'
let x = {Value= {1..10}; Computation= (fun x->x*2)}
let y = {Value= {(1.0)..(10.0)}; Computation= (fun x->x*x)}
let totalX = calculateAndCombine x //this causes the code force 'v to be int
let totalY = calculateAndCombine y //gives an error since this isn't an int
This seems similar to F# generics / function overloading syntax but it didn't really explain how to get it to work for all value types.
I got it to work like this. Read the answer linked by Foggy Finder. Then you need additionally the static member Zero for the sum to work.
type ValueWithComputation< ^T when ^T: (static member (+): ^T * ^T -> ^T) and ^T: (static member Zero: ^T)> =
{ Value: seq< ^T>
Computation: ^T -> ^T }
let inline calculateAndCombine x =
x.Value
|> Seq.map x.Computation
|> Seq.sum
let x = {Value= {1..10}; Computation= (fun x->x*2)}
let y = {Value= {(1.0)..(10.0)}; Computation= (fun x->x*x)}
let totalX = calculateAndCombine x
let totalY = calculateAndCombine y
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.
Let's say I have a bunch of vector types (a la XNA) and some of them have static member Cross:
type Vector3 =
...
static member Cross (a : Vector3, b : Vector3) = new Vector3(...)
I can define the cross function and it compiles:
let inline cross (x : ^T) (y : ^T) = (^T : (static member Cross : (^T * ^T) -> ^T) ((x,y)))
Unfortunately I'm not able to use it and have following error:
let res = cross a b
^
The member or object constructor Cross
takes 2 argument(s) but is here given
1. The required signature is static member Vector3.Cross :
a:Vector3 * b:Vector3 ->
Vector3
Is it even possible at all? Thanks for helping!
You've over-parenthesized your static member signature. Try this instead:
let inline cross (x : ^T) (y : ^T) =
(^T : (static member Cross : ^T * ^T -> ^T) (x,y))
Given your definition, F# was looking for a member Cross which takes a single argument of tuple type.
I found that:
abs -10
abs -10L
both work. So I wondered how F# implemented this and did a search in the source code:
type AbsDynamicImplTable<'T>() =
let AbsDynamic x = AbsDynamicImplTable<_>.Result x
[<CompiledName("Abs")>]
let inline abs (x: ^T) : ^T =
AbsDynamic x
when ^T : ^T = absImpl x
And I am confused with these.
As I know in a function like abs, we must compare the input with 0, and there are different 0s for different types.
Thanks.
To add some explanation to the code posted by ChaosPandion, the problem with F# functions like abs is that they can work with any numeric type. There is no way to express this just using F#/.NET generics - the only supported constrains are that type parameter implements a certain interface or has a constructor, but there is no constraint for numeric types.
So, F# also supports static constraints (the type parameter is ^a instead of usual 'a) and these are processed at compile time using inlining (this also explains why the function has to be inline). You can write you own function with static constraints by using built-in functions from LanguagePrimitives which contains many useful functions that require some constraints:
> let inline half (num: ^a) : ^a =
LanguagePrimitives.DivideByInt< (^a) > num 2
;;
val inline half : ^a -> ^a
when ^a : (static member DivideByInt : ^a * int -> ^a)
> half 42.0;;
val it : float = 21.0
> half 42.0f;;
val it : float32 = 21.0f
Note that constraints are inferred - DivideByInt requires that the type has DivideByInt member, so our function requires the same thing (and it will work with your own type if it has this member too, which is quite useful!).
In addition to this, the implementation of abs uses two additional tricks that are allowed only in the F# library - it specifies different code (to be used when inlining) for different types (using when ^a:int = ....) and a fallback case, which uses Abs member, so it will work with any explicitly listed type or a type with Abs member. Another trick is the retype function, which "changes the type", but doesn't contain any code - the only purpose is to make the code type-check, but this could be very unsafe - so this is used only in F# libraries.
Actually that Abs table will call this:
let inline abs_impl (x: ^a) : ^a =
(^a: (static member Abs : ^a -> ^a) (x))
when ^a : int32 = let x : int32 = retype x in if x >= 0 then x else -x
when ^a : float = let x : float = retype x in if x >= 0.0 then x else -x
when ^a : float32 = let x : float32 = retype x in if x >= 0.0f then x else -x
when ^a : int64 = let x : int64 = retype x in if x >= 0L then x else -x
when ^a : nativeint = let x : nativeint = retype x in if x >= 0n then x else -x
when ^a : int16 = let x : int16 = retype x in if x >= 0s then x else -x
when ^a : sbyte = let x : sbyte = retype x in if x >= 0y then x else -x
when ^a : decimal = System.Math.Abs(retype x : decimal)