Object Expression as Computation Builder - f#

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?

Related

Assign a name to a set of type constraints in F#?

Suppose I have a generic type with some complex type constraints in F#:
[<Struct>]
type Vec2<'t when 't : equality
and 't : comparison
and 't : (static member get_Zero : Unit -> 't)
and 't : (static member (+) : 't * 't -> 't)
and 't : (static member (-) : 't * 't -> 't)
and 't : (static member (*) : 't * 't -> 't)
and 't : (static member (/) : 't * 't -> 't)> =
{
X : 't
Y : 't
}
Now I want to create another generic type that builds on this:
// Does not work
[<Struct>]
type AABB<'t> =
{
Min : Vec2<'t>
Max : Vec2<'t>
}
This does not work unless I replicate the type constraints:
[<Struct>]
type AABB<'t when 't : equality
and 't : comparison
and 't : (static member get_Zero : Unit -> 't)
and 't : (static member (+) : 't * 't -> 't)
and 't : (static member (-) : 't * 't -> 't)
and 't : (static member (*) : 't * 't -> 't)
and 't : (static member (/) : 't * 't -> 't)> =
{
Min : Vec2<'t>
Max : Vec2<'t>
}
This gets old fast!
Is there a way to bind the type constraints to a name so that I can reuse them throughout my code?
// Not real code
constraint IsNumeric 't =
't : equality
and 't : comparison
and 't : (static member get_Zero : Unit -> 't)
and 't : (static member (+) : 't * 't -> 't)
and 't : (static member (-) : 't * 't -> 't)
and 't : (static member (*) : 't * 't -> 't)
and 't : (static member (/) : 't * 't -> 't)
[<Struct>]
type Vec2<'t when IsNumeric 't> =
{
X : 't
Y : 't
}
[<Struct>]
type AABB<'t when IsNumeric 't> =
{
Min : Vec2<'t>
Max : Vec2<'t>
}
A reasonable workaround in this case is to create an interface that represents the constraints. This does not automatically work as named constraint, but you can define a helper function that captures the required operations and then pass the interface around (so that you can invoke the operations you want).
Let's say we want just addition and multiplication:
type INumericalOps<'T> =
abstract Add : 'T * 'T -> 'T
abstract Mul : 'T * 'T -> 'T
[<Struct>]
type Vec2<'T> =
{ X : 'T
Y : 'T }
[<Struct>]
type AABB<'T, 'O when 'O :> INumericalOps<'T>> =
{ Min : Vec2<'T>
Max : Vec2<'T>
Ops : 'O }
Now, the AABB type also contains an implementation of the INumericalOps interface, which is somewhat shorter than specifying all the constraints. We can create an inline function that captures the implementation of * and + for any type that supports those:
let inline capture () =
{ new INumericalOps<_> with
member x.Add(a, b) = a + b
member x.Mul(a, b) = a * b }
When creating a value, the type inference will make sure we get the right implementation of numerical operations:
let aabb =
{ Min = { X = 1.0; Y = 2.0 }
Max = { X = 1.0; Y = 2.0 }
Ops = capture() }
I believe this is essentially this request https://github.com/fsharp/fslang-suggestions/issues/641
Short answer, not supported in F# at this stage but I think it would be a great feature to add.
Your example looks like generic math. This thing will be supported in net6 as preview feature. This has advantages over all others approaches: native support in runtime, C# support it (more libs available), no runtime cost (performance is same as with direct call)
Even though there's no support for this in F# yet, it could look like this (not final, syntax may change)
type IAdditionOperators<'a> =
static abstract (+) : 'a * 'a -> 'a
type IAdditionIdentity<'a> =
inherit IAdditionOperators<'a>
static abstract Zero : 'a
/// This vector type supports addition of 2 generic values
[<Struct>]
type Vector2<'a when 'a : IAdditionIdentity<'a>>(x, y) =
member val X = x
member val Y = y
static member (+) (left, right) =
Vector<'a>('a.(+)(left.X, right.X), 'a.(+)(left.Y, right.Y))
/// This vector supports all methods from INumber
[<Struct>]
type RichVector2<'a when 'a : INumber<'a>>(x, y) =
member val X = x
member val Y = y
static member (+) (left, right) =
Vector<'a>('a.(+)(left.X, right.X), 'a.(+)(left.Y, right.Y))
interface INumber<'a> with
static member (+) (left, right) =
RichVector<'a>.(+)(left, right)
// Requires implementation for +, -, *, /, DivRem,
// Zero, One, Abs, Max, Min, Sign, Clamp,
// Create, CreateSaturating, CreateTruncating, TryCreate,
// Parse, TryParse

Static member constraint for op_addition

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

How this type annotation works, and why the other one does not?

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.

Null Coalescing Operator in F#?

When interacting with C# libraries, I find myself wanting C#'s null coalescing operator both for Nullable structs and reference types.
Is it possible to approximate this in F# with a single overloaded operator that inlines the appropriate if case?
Yes, using some minor hackery found in this SO answer "Overload operator in F#".
At compiled time the correct overload for an usage of either ('a Nullable, 'a) ->'a or ('a when 'a:null, 'a) -> 'a for a single operator can be inlined. Even ('a option, 'a) -> 'a can be thrown in for more flexibility.
To provide closer behavior to c# operator, I've made default parameter 'a Lazy so that it's source isn't called unless the original value is null.
Example:
let value = Something.PossiblyNullReturned()
|?? lazy new SameType()
Implementation:
NullCoalesce.fs [Gist]:
//https://gist.github.com/jbtule/8477768#file-nullcoalesce-fs
type NullCoalesce =
static member Coalesce(a: 'a option, b: 'a Lazy) =
match a with
| Some a -> a
| _ -> b.Value
static member Coalesce(a: 'a Nullable, b: 'a Lazy) =
if a.HasValue then a.Value
else b.Value
static member Coalesce(a: 'a when 'a:null, b: 'a Lazy) =
match a with
| null -> b.Value
| _ -> a
let inline nullCoalesceHelper< ^t, ^a, ^b, ^c when (^t or ^a) : (static member Coalesce : ^a * ^b -> ^c)> a b =
// calling the statically inferred member
((^t or ^a) : (static member Coalesce : ^a * ^b -> ^c) (a, b))
let inline (|??) a b = nullCoalesceHelper<NullCoalesce, _, _, _> a b
Alternatively I made a library that utilizes this technique as well as computation expression for dealing with Null/Option/Nullables, called FSharp.Interop.NullOptAble
It uses the operator |?-> instead.
modified the accepted answer by jbtule to support DBNull:
//https://gist.github.com/tallpeak/7b8beacc8c273acecb5e
open System
let inline isNull value = obj.ReferenceEquals(value, null)
let inline isDBNull value = obj.ReferenceEquals(value, DBNull.Value)
type NullCoalesce =
static member Coalesce(a: 'a option, b: 'a Lazy) = match a with Some a -> a | _ -> b.Value
static member Coalesce(a: 'a Nullable, b: 'a Lazy) = if a.HasValue then a.Value else b.Value
//static member Coalesce(a: 'a when 'a:null, b: 'a Lazy) = match a with null -> b.Value | _ -> a // overridden, so removed
static member Coalesce(a: DBNull, b: 'b Lazy) = b.Value //added to support DBNull
// The following line overrides the definition for "'a when 'a:null"
static member Coalesce(a: obj, b: 'b Lazy) = if isDBNull a || isNull a then b.Value else a // support box DBNull
let inline nullCoalesceHelper< ^t, ^a, ^b, ^c when (^t or ^a) : (static member Coalesce : ^a * ^b -> ^c)> a b =
((^t or ^a) : (static member Coalesce : ^a * ^b -> ^c) (a, b))
Usage:
let inline (|??) a b = nullCoalesceHelper<NullCoalesce, _, _, _> a b
let o = box null
let x = o |?? lazy (box 2)
let y = (DBNull.Value) |?? lazy (box 3)
let z = box (DBNull.Value) |?? lazy (box 4)
let a = None |?? lazy (box 5)
let b = box None |?? lazy (box 6)
let c = (Nullable<int>() ) |?? lazy (7)
let d = box (Nullable<int>() ) |?? lazy (box 8)
I usually use defaultArg for this purpose as it is built-in to the language.

How to write a function for generic numbers?

I'm quite new to F# and find type inference really is a cool thing. But currently it seems that it also may lead to code duplication, which is not a cool thing. I want to sum the digits of a number like this:
let rec crossfoot n =
if n = 0 then 0
else n % 10 + crossfoot (n / 10)
crossfoot 123
This correctly prints 6. But now my input number does not fit int 32 bits, so I have to transform it to.
let rec crossfoot n =
if n = 0L then 0L
else n % 10L + crossfoot (n / 10L)
crossfoot 123L
Then, a BigInteger comes my way and guess what…
Of course, I could only have the bigint version and cast input parameters up and output parameters down as needed. But first I assume using BigInteger over int has some performance penalities. Second let cf = int (crossfoot (bigint 123)) does just not read nice.
Isn't there a generic way to write this?
Building on Brian's and Stephen's answers, here's some complete code:
module NumericLiteralG =
let inline FromZero() = LanguagePrimitives.GenericZero
let inline FromOne() = LanguagePrimitives.GenericOne
let inline FromInt32 (n:int) =
let one : ^a = FromOne()
let zero : ^a = FromZero()
let n_incr = if n > 0 then 1 else -1
let g_incr = if n > 0 then one else (zero - one)
let rec loop i g =
if i = n then g
else loop (i + n_incr) (g + g_incr)
loop 0 zero
let inline crossfoot (n:^a) : ^a =
let (zero:^a) = 0G
let (ten:^a) = 10G
let rec compute (n:^a) =
if n = zero then zero
else ((n % ten):^a) + compute (n / ten)
compute n
crossfoot 123
crossfoot 123I
crossfoot 123L
UPDATE: Simple Answer
Here's a standalone implementation, without the NumericLiteralG module, and a slightly less restrictive inferred type:
let inline crossfoot (n:^a) : ^a =
let zero:^a = LanguagePrimitives.GenericZero
let ten:^a = (Seq.init 10 (fun _ -> LanguagePrimitives.GenericOne)) |> Seq.sum
let rec compute (n:^a) =
if n = zero then zero
else ((n % ten):^a) + compute (n / ten)
compute n
Explanation
There are effectively two types of generics in F#: 1) run-type polymorphism, via .NET interfaces/inheritance, and 2) compile time generics. Compile-time generics are needed to accommodate things like generic numerical operations and something like duck-typing (explicit member constraints). These features are integral to F# but unsupported in .NET, so therefore have to be handled by F# at compile time.
The caret (^) is used to differentiate statically resolved (compile-time) type parameters from ordinary ones (which use an apostrophe). In short, 'a is handled at run-time, ^a at compile-time–which is why the function must be marked inline.
I had never tried to write something like this before. It turned out clumsier than I expected. The biggest hurdle I see to writing generic numeric code in F# is: creating an instance of a generic number other than zero or one. See the implementation of FromInt32 in this answer to see what I mean. GenericZero and GenericOne are built-in, and they're implemented using techniques that aren't available in user code. In this function, since we only needed a small number (10), I created a sequence of 10 GenericOnes and summed them.
I can't explain as well why all the type annotations are needed, except to say that it appears each time the compiler encounters an operation on a generic type it seems to think it's dealing with a new type. So it ends up inferring some bizarre type with duplicated resitrictions (e.g. it may require (+) multiple times). Adding the type annotations lets it know we're dealing with the same type throughout. The code works fine without them, but adding them simplifies the inferred signature.
In addition to kvb's technique using Numeric Literals (Brian's link), I've had a lot of success using a different technique which can yield better inferred structural type signatures and may also be used to create precomputed type-specific functions for better performance as well as control over supported numeric types (since you will often want to support all integral types, but not rational types, for example): F# Static Member Type Constraints.
Following up on the discussion Daniel and I have been having about the inferred type signatures yielded by the different techniques, here is an overview:
NumericLiteralG Technique
module NumericLiteralG =
let inline FromZero() = LanguagePrimitives.GenericZero
let inline FromOne() = LanguagePrimitives.GenericOne
let inline FromInt32 (n:int) =
let one = FromOne()
let zero = FromZero()
let n_incr = if n > 0 then 1 else -1
let g_incr = if n > 0 then one else (zero - one)
let rec loop i g =
if i = n then g
else loop (i + n_incr) (g + g_incr)
loop 0 zero
Crossfoot without adding any type annotations:
let inline crossfoot1 n =
let rec compute n =
if n = 0G then 0G
else n % 10G + compute (n / 10G)
compute n
val inline crossfoot1 :
^a -> ^e
when ( ^a or ^b) : (static member ( % ) : ^a * ^b -> ^d) and
^a : (static member get_Zero : -> ^a) and
( ^a or ^f) : (static member ( / ) : ^a * ^f -> ^a) and
^a : equality and ^b : (static member get_Zero : -> ^b) and
( ^b or ^c) : (static member ( - ) : ^b * ^c -> ^c) and
( ^b or ^c) : (static member ( + ) : ^b * ^c -> ^b) and
^c : (static member get_One : -> ^c) and
( ^d or ^e) : (static member ( + ) : ^d * ^e -> ^e) and
^e : (static member get_Zero : -> ^e) and
^f : (static member get_Zero : -> ^f) and
( ^f or ^g) : (static member ( - ) : ^f * ^g -> ^g) and
( ^f or ^g) : (static member ( + ) : ^f * ^g -> ^f) and
^g : (static member get_One : -> ^g)
Crossfoot adding some type annotations:
let inline crossfoot2 (n:^a) : ^a =
let (zero:^a) = 0G
let (ten:^a) = 10G
let rec compute (n:^a) =
if n = zero then zero
else ((n % ten):^a) + compute (n / ten)
compute n
val inline crossfoot2 :
^a -> ^a
when ^a : (static member get_Zero : -> ^a) and
( ^a or ^a0) : (static member ( - ) : ^a * ^a0 -> ^a0) and
( ^a or ^a0) : (static member ( + ) : ^a * ^a0 -> ^a) and
^a : equality and ^a : (static member ( + ) : ^a * ^a -> ^a) and
^a : (static member ( % ) : ^a * ^a -> ^a) and
^a : (static member ( / ) : ^a * ^a -> ^a) and
^a0 : (static member get_One : -> ^a0)
Record Type Technique
module LP =
let inline zero_of (target:'a) : 'a = LanguagePrimitives.GenericZero<'a>
let inline one_of (target:'a) : 'a = LanguagePrimitives.GenericOne<'a>
let inline two_of (target:'a) : 'a = one_of(target) + one_of(target)
let inline three_of (target:'a) : 'a = two_of(target) + one_of(target)
let inline negone_of (target:'a) : 'a = zero_of(target) - one_of(target)
let inline any_of (target:'a) (x:int) : 'a =
let one:'a = one_of target
let zero:'a = zero_of target
let xu = if x > 0 then 1 else -1
let gu:'a = if x > 0 then one else zero-one
let rec get i g =
if i = x then g
else get (i+xu) (g+gu)
get 0 zero
type G<'a> = {
negone:'a
zero:'a
one:'a
two:'a
three:'a
any: int -> 'a
}
let inline G_of (target:'a) : (G<'a>) = {
zero = zero_of target
one = one_of target
two = two_of target
three = three_of target
negone = negone_of target
any = any_of target
}
open LP
Crossfoot, no annotations required for nice inferred signature:
let inline crossfoot3 n =
let g = G_of n
let ten = g.any 10
let rec compute n =
if n = g.zero then g.zero
else n % ten + compute (n / ten)
compute n
val inline crossfoot3 :
^a -> ^a
when ^a : (static member ( % ) : ^a * ^a -> ^b) and
( ^b or ^a) : (static member ( + ) : ^b * ^a -> ^a) and
^a : (static member get_Zero : -> ^a) and
^a : (static member get_One : -> ^a) and
^a : (static member ( + ) : ^a * ^a -> ^a) and
^a : (static member ( - ) : ^a * ^a -> ^a) and ^a : equality and
^a : (static member ( / ) : ^a * ^a -> ^a)
Crossfoot, no annotations, accepts precomputed instances of G:
let inline crossfootG g ten n =
let rec compute n =
if n = g.zero then g.zero
else n % ten + compute (n / ten)
compute n
val inline crossfootG :
G< ^a> -> ^b -> ^a -> ^a
when ( ^a or ^b) : (static member ( % ) : ^a * ^b -> ^c) and
( ^c or ^a) : (static member ( + ) : ^c * ^a -> ^a) and
( ^a or ^b) : (static member ( / ) : ^a * ^b -> ^a) and
^a : equality
I use the above in practice since then I can make precomputed type specific versions which don't suffer from the performance cost of Generic LanguagePrimitives:
let gn = G_of 1 //int32
let gL = G_of 1L //int64
let gI = G_of 1I //bigint
let gD = G_of 1.0 //double
let gS = G_of 1.0f //single
let gM = G_of 1.0m //decimal
let crossfootn = crossfootG gn (gn.any 10)
let crossfootL = crossfootG gL (gL.any 10)
let crossfootI = crossfootG gI (gI.any 10)
let crossfootD = crossfootG gD (gD.any 10)
let crossfootS = crossfootG gS (gS.any 10)
let crossfootM = crossfootG gM (gM.any 10)
Since the question of how to make the type signatures less hairy when using the generalized numeric literals has come up, I thought I'd put in my two cents. The main issue is that F#'s operators can be asymmetric so that you can do stuff like System.DateTime.Now + System.TimeSpan.FromHours(1.0), which means that F#'s type inference adds intermediary type variables whenever arithmetic operations are being performed.
In the case of numerical algorithms, this potential asymmetry isn't typically useful and the resulting explosion in the type signatures is quite ugly (although it generally doesn't affect F#'s ability to apply the functions correctly when given concrete arguments). One potential solution to this problem is to restrict the types of the arithmetic operators within the scope that you care about. For instance, if you define this module:
module SymmetricOps =
let inline (+) (x:'a) (y:'a) : 'a = x + y
let inline (-) (x:'a) (y:'a) : 'a = x - y
let inline (*) (x:'a) (y:'a) : 'a = x * y
let inline (/) (x:'a) (y:'a) : 'a = x / y
let inline (%) (x:'a) (y:'a) : 'a = x % y
...
then you can just open the SymmetricOps module whenever you want have the operators apply only to two arguments of the same type. So now we can define:
module NumericLiteralG =
open SymmetricOps
let inline FromZero() = LanguagePrimitives.GenericZero
let inline FromOne() = LanguagePrimitives.GenericOne
let inline FromInt32 (n:int) =
let one = FromOne()
let zero = FromZero()
let n_incr = if n > 0 then 1 else -1
let g_incr = if n > 0 then one else (zero - one)
let rec loop i g =
if i = n then g
else loop (i + n_incr) (g + g_incr)
loop 0 zero
and
open SymmetricOps
let inline crossfoot x =
let rec compute n =
if n = 0G then 0G
else n % 10G + compute (n / 10G)
compute x
and the inferred type is the relatively clean
val inline crossfoot :
^a -> ^a
when ^a : (static member ( - ) : ^a * ^a -> ^a) and
^a : (static member get_One : -> ^a) and
^a : (static member ( % ) : ^a * ^a -> ^a) and
^a : (static member get_Zero : -> ^a) and
^a : (static member ( + ) : ^a * ^a -> ^a) and
^a : (static member ( / ) : ^a * ^a -> ^a) and ^a : equality
while we still get the benefit of a nice, readable definition for crossfoot.
I stumbled upon this topic when I was looking for a solution and I am posting my answer, because I found a way to express generic numeral without the less than optimal implementation of building up the number by hand.
open System.Numerics
// optional
open MathNet.Numerics
module NumericLiteralG =
type GenericNumber = GenericNumber with
static member instance (GenericNumber, x:int32, _:int8) = fun () -> int8 x
static member instance (GenericNumber, x:int32, _:uint8) = fun () -> uint8 x
static member instance (GenericNumber, x:int32, _:int16) = fun () -> int16 x
static member instance (GenericNumber, x:int32, _:uint16) = fun () -> uint16 x
static member instance (GenericNumber, x:int32, _:int32) = fun () -> x
static member instance (GenericNumber, x:int32, _:uint32) = fun () -> uint32 x
static member instance (GenericNumber, x:int32, _:int64) = fun () -> int64 x
static member instance (GenericNumber, x:int32, _:uint64) = fun () -> uint64 x
static member instance (GenericNumber, x:int32, _:float32) = fun () -> float32 x
static member instance (GenericNumber, x:int32, _:float) = fun () -> float x
static member instance (GenericNumber, x:int32, _:bigint) = fun () -> bigint x
static member instance (GenericNumber, x:int32, _:decimal) = fun () -> decimal x
static member instance (GenericNumber, x:int32, _:Complex) = fun () -> Complex.op_Implicit x
static member instance (GenericNumber, x:int64, _:int64) = fun () -> int64 x
static member instance (GenericNumber, x:int64, _:uint64) = fun () -> uint64 x
static member instance (GenericNumber, x:int64, _:float32) = fun () -> float32 x
static member instance (GenericNumber, x:int64, _:float) = fun () -> float x
static member instance (GenericNumber, x:int64, _:bigint) = fun () -> bigint x
static member instance (GenericNumber, x:int64, _:decimal) = fun () -> decimal x
static member instance (GenericNumber, x:int64, _:Complex) = fun () -> Complex.op_Implicit x
static member instance (GenericNumber, x:string, _:float32) = fun () -> float32 x
static member instance (GenericNumber, x:string, _:float) = fun () -> float x
static member instance (GenericNumber, x:string, _:bigint) = fun () -> bigint.Parse x
static member instance (GenericNumber, x:string, _:decimal) = fun () -> decimal x
static member instance (GenericNumber, x:string, _:Complex) = fun () -> Complex(float x, 0.0)
// MathNet.Numerics
static member instance (GenericNumber, x:int32, _:Complex32) = fun () -> Complex32.op_Implicit x
static member instance (GenericNumber, x:int32, _:bignum) = fun () -> bignum.FromInt x
static member instance (GenericNumber, x:int64, _:Complex32) = fun () -> Complex32.op_Implicit x
static member instance (GenericNumber, x:int64, _:bignum) = fun () -> bignum.FromBigInt (bigint x)
static member instance (GenericNumber, x:string, _:Complex32) = fun () -> Complex32(float32 x, 0.0f)
static member instance (GenericNumber, x:string, _:bignum) = fun () -> bignum.FromBigInt (bigint.Parse x)
let inline genericNumber num = Inline.instance (GenericNumber, num) ()
let inline FromZero () = LanguagePrimitives.GenericZero
let inline FromOne () = LanguagePrimitives.GenericOne
let inline FromInt32 n = genericNumber n
let inline FromInt64 n = genericNumber n
let inline FromString n = genericNumber n
this implementation comes by without complicated iteration during the cast. It uses FsControl for the Instance module.
http://www.fssnip.net/mv
Is crossfoot exactly what you want to do, or is it just summing the digits of a long number?
because if you just want to sum the digits, then:
let crossfoot (x:'a) = x.ToString().ToCharArray()
|> (Array.fold(fun acc x' -> if x' <> '.'
then acc + (int x')
else acc) 0)
... And you are done.
Anyways,
Can you convert stuff to a string, drop the decimal point, remember where the decimal point is, interpret it as an int, run crossfoot?
Here is my solution. I am not sure exactly how you want "crossfoot" to work when you have a decimal point added.
For instance, do you want: crossfoot(123.1) = 7 or crossfoot(123.1) = 6.1? (I'm assuming you want the latter)
Anyways, the code does allow you to work with numbers as generics.
let crossfoot (n:'a) = // Completely generic input
let rec crossfoot' (a:int) = // Standard integer crossfoot
if a = 0 then 0
else a%10 + crossfoot' (a / 10)
let nstr = n.ToString()
let nn = nstr.Split([|'.'|]) // Assuming your main constraint is float/int
let n',n_ = if nn.Length > 1 then nn.[0],nn.[1]
else nn.[0],"0"
let n'',n_' = crossfoot'(int n'),crossfoot'(int n_)
match n_' with
| 0 -> string n''
| _ -> (string n'')+"."+(string n_')
If you need to input big integers or int64 stuff, the way crossfoot works, you can just split the big number into bitesize chunks (strings) and feed them into this function, and add them together.

Resources