I have this question about matching functions by its signature. I am asking this for learning purposes and I do understand that there are many ways of going around this.
Suppose that in F# I have the following analogous types:
type T1 = int * int * int
type T2 = {a:int ; b:int; c:int}
And the conversion between them is as trivial as
let convert p =
match p with
|(x,y,z) -> {a = x; b = y; c = z}
Suppose also that I have a substantial library of functions that were written for using with T1 that I now want to reuse with T2, such as:
let addT1 a b =
match a, b with
|(x1,y1,z1),(x2,y2,z2) -> (x1 + x2, y1 + y2, z1 + z2)
let negT1 =
function
|(x,y,z) -> (-x,-y-z)
let offset a i =
match a with
|(x,y,z) -> (x + i, y + i, z + i)
As you can see, all functions rely on T1 but may get multiple parameters of T1 or parameters of other types.
I am familiar with this, witch allows me to map any function (f: int * int * int -> 'a) to a function (g: T2 -> 'a):
let funcOfT2 f = fun (t:T2) -> f (t.a, t.b, t.c)
But in the case of addT1, for example, this does not work because of the second parameter:
val addT1 : int * int * int -> int * int * int -> int * int * int
val (funcOfT2 addt1) : (T2 -> int * int * int -> int * int * int)
So, to use a function like funcOfT2 I would have to use it like this, witch is impractical:
let p1 = {a = 1; b = 2; c = 3}
let p2 = {a = 2; b = 3; c = 4}
let x = funcOfT2 (funcOfT2 addT1 p1) p2
val x = int * int * int = (3,5,7)
I could also make a local version of each function of T1 passing the necessary amounts of funcOfT2 or use convert every time I use a Function of T1, but I believe it would be really impractical and make a cluttered code.
Is there anyway of matching the signature of a function so that I can convert any function that takes any T1 to a function of T2?
My idea was something like this, it does not work for many reasons, but I think it may exemplify what I wanted. Is there any way to do it?:
let rec myDream f =
match f with
|(g: T1 -> 'a) -> fun (t:T2) rest -> fOfT2 (g (convert t)) rest
|(g: 'b -> 'c) -> fun x y -> fOfT2 (g x) y
|(g: 'd) -> g d
Related
type Interpreter<'a> =
| RegularInterpreter of (int -> 'a)
| StringInterpreter of (string -> 'a)
let add<'a> (x: 'a) (y: 'a) (in_: Interpreter<'a>): 'a =
match in_ with
| RegularInterpreter r ->
x+y |> r
| StringInterpreter r ->
sprintf "(%s + %s)" x y |> r
The error message of it not being able to resolve 'a at compile time is pretty clear to me. I am guessing that the answer to the question of whether it is possible to make the above work is no, short of adding functions directly into the datatype. But then I might as well use an interface, or get rid of generic parameters entirely.
Edit: Mark's reply does in fact do what I asked, but let me extend the question as I did not explain it adequately. What I am trying to do is do with the technique above is imitate what what was done in this post. The motivation for this is to avoid inlined functions as they have poor composability - they can't be passed as lambdas without having their generic arguments specialized.
I was hoping that I might be able to work around it by passing an union type with a generic argument into a closure, but...
type Interpreter<'a> =
| RegularInterpreter of (int -> 'a)
| StringInterpreter of (string -> 'a)
let val_ x in_ =
match in_ with
| RegularInterpreter r -> r x
| StringInterpreter r -> r (string x)
let inline add x y in_ =
match in_ with
| RegularInterpreter r ->
x in_ + y in_ |> r
| StringInterpreter r ->
sprintf "(%A + %A)" (x in_) (y in_) |> r
let inline mult x y in_ =
match in_ with
| RegularInterpreter r ->
x in_ * y in_ |> r
| StringInterpreter r ->
sprintf "(%A * %A)" (x in_) (y in_) |> r
let inline r2 in_ = add (val_ 1) (val_ 3) in_
r2 (RegularInterpreter id)
r2 (StringInterpreter id) // Type error.
This last line gives a type error. Is there a way around this? Though I'd prefer the functions to not be inlined due to the limits they place on composability.
Remove the type annotations:
let inline add x y in_ =
match in_ with
| RegularInterpreter r ->
x + y |> r
| StringInterpreter r ->
sprintf "(%A + %A)" x y |> r
You'll also need to make a few other changes, which I've also incorporated above:
Change the format specifiers used with sprintf to something more generic. When you use %s, you're saying that the argument for that placeholder must be a string, so the compiler would infer x and y to be string values.
Add the inline keyword.
With these changes, the inferred type of add is now:
x: ^a -> y: ^b -> in_:Interpreter<'c> -> 'c
when ( ^a or ^b) : (static member ( + ) : ^a * ^b -> int)
You'll notice that it works for any type where + is defined as turning the input arguments into int. In practice, that's probably going to mean only int itself, unless you define a custom operator.
FSI smoke tests:
> add 3 2 (RegularInterpreter id);;
val it : int = 5
> add 2 3 (StringInterpreter (fun _ -> 42));;
val it : int = 42
The compiler ends up defaulting to int, and the kind of polymorphism you want is difficult to achieve in F#. This article articulates the point.
Perhaps, you could work the dark arts using FSharp.Interop.Dynamic but you lose compile time checking which sort of defeats the point.
I've come to the conclusion that what I am trying to is impossible. I had a hunch that it was already, but the proof is in the following:
let vale (x,_,_) = x
let adde (_,x,_) = x
let multe (_,_,x) = x
let val_ x d =
let f = vale d
f x
let add x y d =
let f = adde d
f (x d) (y d)
let mult x y d =
let f = multe d
f (x d) (y d)
let in_1 =
let val_ (x: int) = x
let add x y = x+y
let mult x y = x*y
val_,add,mult
let in_2 =
let val_ (x: int) = string x
let add x y = sprintf "(%s + %s)" x y
let mult x y = sprintf "(%s * %s)" x y
val_,add,mult
let r2 d = add (val_ 1) (val_ 3) d
//let test x = x in_1, x in_2 // Type error.
let a2 = r2 in_1 // Works
let b2 = r2 in_2 // Works
The reasoning goes that if it cannot be done with plain functions passed as arguments, then it definitely won't be possible with interfaces, records, discriminated unions or any other scheme. The standard functions are more generic than any of the above, and if they cannot do it then this is a fundamental limitation of the language.
It is not the lack of HKTs that make the code ungeneric, but something as simple as this. In fact, going by the Finally Tagless paper linked to in the Reddit post, Haskell has the same problem with needing to duplicate interpreters without the impredicative types extension - though I've looked around and it seem that impredicative types will be removed in the future as the extension is difficult to maintain.
Nevertheless, I do hope this is only a current limitation of F#. If the language was dynamic, the code segment above would in fact run correctly.
Unfortunately, it's not completely clear to me what you're trying to do. However, it seems likely that it's possible by creating an interface with a generic method. For example, here's how you could get the code from your answer to work:
type I = abstract Apply : ((int -> 'a) * ('a -> 'a -> 'a) * ('a -> 'a -> 'a)) -> 'a
//let test x = x in_1, x in_2 // Type error.
let test (i:I) = i.Apply in_1, i.Apply in_2
let r2' = { new I with member __.Apply d = add (val_ 1) (val_ 3) d }
test r2' // no problem
If you want to use a value (e.g. a function input) generically, then in most cases the cleanest way is to create an interface with a generic method whose signature expresses the required polymorphism.
In F# there are many different collection-looking syntaxes that compile into something. What are they all, and what do they mean?
let s1 = [a, b, c, d]
let s2 = [a; b; c; d]
let s3 = (a, b, c, d)
let s4 = (a, b, c, d)
let s5 = [|a, b, c, d|]
let s6 = [|a; b; c; d|]
let s7 = a, b, c, d
let s8 = { aa = 3; bb = 4 }
[a, b, c, d] is a list with a single 4-tuple as element.
[a; b; c; d] is a four-element list.
(a, b, c, d) is a 4-tuple.
[|a, b, c, d|] is an array with a single 4-tuple as element.
[|a; b; c; d|] is a four-element array.
a, b, c, d is a 4-tuple.
{ aa = 3; bb = 4 } is a value of a record type with two fields aa and bb.
Answer provided by райтфолд gives you answer, I'll try give you a way to get answer by yourself next time you have a question like this.
The easiest way is to use F# interactive (you can start it from Visual Studio from View -> Other Windows -> F# Interactive). Just type F# code, add double semicolon ;; and hit ENTER. To make your declarations work you have to declare a, b, c and d first. Let's make them all integers:
> let a = 1
let b = 2
let c = 3
let d = 4
;;
val a : int = 1
val b : int = 2
val c : int = 3
val d : int = 4
Now you can try your declarations:
> let s1 = [a, b, c, d];;
val s1 : (int * int * int * int) list = [(1, 2, 3, 4)]
F# Interactive prints back the type of expression that was evaluated. In that case it's (int * int * int * int) list. How to read that? * is used to divide elements of a tuple type, so (int * int * int * int) means a tuple with four elements, all types as int. Following list means a list of those. So (int * int * int * int) list is a list of tuples with four int-typed elements each.
> let s2 = [a; b; c; d];;
val s2 : int list = [1; 2; 3; 4]
Similar concept, this time it's a list of int elements.
> let s3 = (a, b, c, d);;
val s3 : int * int * int * int = (1, 2, 3, 4)
This one was already explained above: int * int * int * int is a four element tuple with all elements typed as int.
> let s5 = [|a, b, c, d|]
let s6 = [|a; b; c; d|];;
val s5 : (int * int * int * int) [] = [|(1, 2, 3, 4)|]
val s6 : int [] = [|1; 2; 3; 4|]
These are very similar to s1 and s2, but instead of list element type is followed by [] - that means it's an array. s5 is an array of (int * int * int * int) elements and s6 is an array of int elements.
> let s7 = a, b, c, d;;
val s7 : int * int * int * int = (1, 2, 3, 4)
Same as s3.
> let s8 = { aa = 3; bb = 4 };;
let s8 = { aa = 3; bb = 4 };;
-----------^^
stdin(18,12): error FS0039: The record label 'aa' is not defined
This one is tricky. To make it work you have to declare record type first:
> type myRecordType = { aa: int; bb: int };;
type myRecordType =
{aa: int;
bb: int;}
with that it works and prints that s8 is an instance of myRecordType:
> let s8 = { aa = 3; bb = 4 };;
val s8 : myRecordType = {aa = 3;
bb = 4;}
let s1 = [a, b, c, d]
Equivalent to [(a, b, c, d)]: a list containing one quadruple (tuple of 4 elements).
let s2 = [a; b; c; d]
A list with 4 elements.
let s3 = (a, b, c, d)
A quadruple.
let s4 = (a, b, c, d)
The same quadruple.
let s5 = [|a, b, c, d|]
Equivalent to [|(a, b, c, d)|]: an array containing one quadruple.
let s6 = [|a; b; c; d|]
An array with 4 elements.
let s7 = a, b, c, d
A quadruple (can omit brackets in this case and when there is no ambiguity).
let s8 = { aa = 3; bb = 4 }
A record definition.
I am trying to create a save/load function for 2d objects that has been drawn into a form.
type circle = { X : int; Y : int; Diameter : int; Brush : Brush}
type Square = { X : int; Y : int; Length : int; Height: int; Brush : Brush}
When i create the object i put them into 2 lists 1 for each type.
My initial thought was to read and write these objects to a textfile, see below:
saveFile.Click.Add(fun _ ->
for c in listOfCircles do
myfile.WriteLine("Circle," + c.X.ToString() + "," + c.Y.ToString() + "," + c.Diameter.ToString() + "," + c.Brush.ToString())
for s in listOfSquares do
myfile.WriteLine("Square," + s.X.ToString() + "," + s.Y.ToString() + "," + s.Height.ToString() + "," + s.Length.ToString() + "," + s.Brush.ToString())
myfile.Close() // close the file
And in the textfile it looks like this
Circle,200,200,50,System.Drawing.SolidBrush
Square,50,55,45,55,System.Drawing.SolidBrush
From here i want to read these values and then be able to parse them and recreate the objects by adding the objects the lists and redraw them.
let readCircle =
System.IO.File.ReadAllLines path
|> Array.choose (fun s ->
match s.Split ',' with
| [|x; y ; z ; b ; _|] when x = "Circle" -> Some (y, z, b)
| _ -> None )
let readSquare =
System.IO.File.ReadAllLines path
|> Array.choose (fun s ->
match s.Split ',' with
| [|x; y ; z ; b ; a ; _|] when x = "Square" -> Some (y, z, b, a)
| _ -> None )
These functions gives me
val readCircle : (string * string * string) [] = [|("200", "200", "50")|]
val readSquare : (string * string * string * string) [] = [|("50", "55", "45", "55")|]
The problem i have now is im not sure how to obtain the values from the array. Beneath is example with multiple circles.
val readCircle : (string * string * string) [] = [|("200", "200", "50"); ("200", "200","50")|]
Any ideas or comments about how to go from here/how to resolve this issue is very appreciated! Question summary: how could i get the values from the array and put them in for example my already created add functions, see below:
listOfCircles.Add({ X = 200; Y = 200; Diameter = 50; Brush = Brushes.Black})
You could convert the arrays of string tuples you have using Array.map, e.g.
[|("200", "200", "50"); ("200", "200","50")|]
|> Array.map (fun (x,y,d) -> {X = int32 x; Y = int32 y; Diameter = int32 d; Brush = Brushes.Black})
It might be a bit clearer if you converted to circle or square as you parsed the file, then you would have an array of circle or an array of square that you can add directly to your lists.
let readCircle =
System.IO.File.ReadAllLines path
|> Array.choose (fun s ->
match s.Split ',' with
| [|t; x; y; d; _|] when t = "Circle" ->
Some {X = int32 x; Y = int32 y; Diameter = int32 d; Brush = Brushes.Red}
| _ -> None )
But... if you wanted to make larger changes, you could use discriminated unions to represent your shapes, they would then share a common type of Shape and you could parse circles and squares in the same function.
type Shape =
| Circle of X : int * Y : int * Diameter : int * Brush : Brush
| Square of X : int * Y : int * Length : int * Height: int * Brush : Brush
let readShapes (data: string array) =
data
|> Array.choose (fun s ->
match s.Split ',' with
| [|t; x; y; d; _|] when t = "Circle" ->
Some (Circle(X = int32 x, Y = int32 y, Diameter = int32 d, Brush = Brushes.Red))
| [|t; x; y; l; h; _|] when t = "Square" ->
Some (Square(X = int32 x, Y = int32 y, Length = int32 l, Height = int32 h, Brush = Brushes.Red))
| _ -> None )
let listOfShapes = ResizeArray<_>()
let testInput = """
Circle,200,200,50,System.Drawing.SolidBrush
Square,50,55,45,55,System.Drawing.SolidBrush"""
testInput.Split('\n') // System.IO.File.ReadAllLines path
|> readShapes
|> Array.iter (listOfShapes.Add)
Which would result in
val it : System.Collections.Generic.List<Shape> =
seq
[Circle (200,200,50,System.Drawing.SolidBrush {Color = Color [Red];});
Square (50,55,45,55,System.Drawing.SolidBrush {Color = Color [Red];})]
You could then use pattern matching to draw each type of shape
let drawShape shape =
match shape with
| Circle(x,y,d,b) ->
printfn "Pretend I just drew a circle at %d,%d with diameter %d." x y d
| Square(x,y,l,h,b) ->
printfn "Pretend I just drew a rectangle at %d,%d that was %d long and %d high." x y l h
listOfShapes |> Seq.iter drawShape
Giving
Pretend I just drew a circle at 200,200 with diameter 50.
Pretend I just drew a rectangle at 50,55 that was 45 long and 55 high.
If I understand your goal, this is how I would go about it. I've only implemented Circle; you'll need to modify it to handle Square.
open System
open System.Collections.Generic
open System.Drawing
open System.IO
let memoize f =
let cache = Dictionary()
fun key ->
match cache.TryGetValue(key) with
| true, value -> value
| _ ->
let value = f key
cache.Add(key, value)
value
let getBrush =
memoize (fun name -> typeof<Brushes>.GetProperty(name).GetValue(null) :?> SolidBrush)
type Circle =
{ X : int
Y : int
Diameter : int
Brush : SolidBrush } with
override this.ToString() =
sprintf "Circle,%d,%d,%d,%s" this.X this.Y this.Diameter this.Brush.Color.Name
static member Parse(s: string) =
match s.Split(',') with
| [|"Circle";x;y;diameter;brushName|] -> {X=int x; Y=int y; Diameter=int diameter; Brush=getBrush brushName}
| _ -> invalidArg "s" "Cannot parse string"
let writeShapesToFile fileName shapes =
File.WriteAllLines(fileName, Seq.map (sprintf "%O") shapes)
let readShapesFromFile fileName =
File.ReadAllLines(fileName) |> Array.map Circle.Parse
Also, you might consider using a class hierarchy instead of records since much of the structure and behavior of Circle and Square are shared.
This is fun - I approached it in a totally different way than Daniel (but I agree with him that you classes might be a better approach for your shapes). Instead, I took advantage of discriminated unions (and there are better ways to do this - more later):
First, I define a type for a list of parameters for making a shape:
type Parameter =
| Label of string
| Number of int
Now let's convert a string to a parameter:
let toParameter s =
match Int32.TryParse(s) with
| (true, i) -> Number(i)
| (_, _) -> Label(s)
Now to convert a list of strings to a list of Parameter:
let stringListToParameterList stringlist = stringlist |> List.map(function s -> toParameter s)
Now to convert a comma-separated string to a list of string:
let commastringToList (s:string) = s.Split(',') |> Array.toList
OK - great - let's define your records and a master Shape:
type circlerec = { X : int; Y : int; Diameter : int; Brush : Brush}
type squarerec = { X : int; Y : int; Length : int; Height: int; Brush : Brush}
type Shape =
| Circle of circlerec
| Square of squarerec
With this we need a way to make a Shape from a parameter list. This is brute force, but it reads well enough:
let toShape list =
match list with
| Label("Circle") :: Number(x) :: Number(y) :: Number(diam) :: Label(colorName) :: [] ->
Circle({X = x; Y = y; Diameter = diam; Brush = new SolidBrush(Color.FromName(colorName)); })
| Label("Circle") :: rest -> raise <| new ArgumentException("parse error:expected Circle num num num color but got " + list.ToString())
| Label("Square") :: Number(x) :: Number(y) :: Number(length) :: Number(height) :: Label(colorName) :: [] ->
Square({X = x; Y = y; Length = length; Height = height; Brush = new SolidBrush(Color.FromName(colorName)); })
| Label("Square") :: rest -> raise <| new ArgumentException("parse error:expected Square num num num num color but got " + list.ToString())
| _ -> raise <| new ArgumentException("parse error: unknown shape: " + list.ToString())
It's dense, but I'm using F#'s pattern matching to spot the various parameters for each shape. Note that you could now do things like have Square,x,y,size,colorName in your file and make a Square where Length and Height are equal to size by just adding in the pattern.
Finally comes the piece de resistance, converting your file into shapes:
let toShapes path =
System.IO.File.ReadAllLines path |> Array.toList |>
List.map(function s -> s |> commastringToList |>
stringListToParameterList |> toShape)
which maps every line in the file to a list of string which then maps each line to a shape, but piping the comma string to the list converter and then through the parameter list and then to a shape.
Now where this is bad is that the error checking is pretty horrid and that the Parameter type should really include Pigment of Color, which would allow you to look at the string that comes in and if it's valid Color name, map it to a Pigment else a Label.
There's a generic function LanguagePrimitives.DivideByInt to divide by int without losing generic behavior, we can use it like this:
let inline Divideby2 n = LanguagePrimitives.DivideByInt n 2
val inline Divideby2 :
^a -> ^a when ^a : (static member DivideByInt : ^a * int -> ^a)
But there's no function called MultiplyByInt to perform generic multiplication by int. Is there anything to perform generic multiplication? Like this:
let inline MultiplyBy2 n = SomeGenericFunctionsModule.MultiplybyInt n 2;
P.S. we can always use some non-standard approach like:
let inline MultiplyByInt n m = seq { for i in 1..m -> n} |> Seq.sum
but I'm interested if it is possible to do in the right way.
I'm afraid there's no built-in function, but I can propose two alternative solutions:
type MulExtension = MulExtension of int with
static member (=>) (x:float , MulExtension y) = x * (float y)
static member (=>) (x:decimal, MulExtension y) = x * (decimal y)
static member (=>) (x:int64 , MulExtension y) = x * (int64 y)
// More overloads
let inline MultiplyByInt x y = x => MulExtension y
But you'll have to specify each type.
I would rather use this function:
let inline MultiplyByInt x y =
let fromInt (n:int) : ^a when ^a : (static member (*) : ^a * ^a -> ^a) =
System.Convert.ChangeType(n, typeof<'a>) :?> 'a
x * (fromInt y)
I can't see any difference in performance between both methods.
I managed to get a solution in O(log(N)) which beats yours, but it still feels very ugly
let inline MulInt (m:^t) (n:int) =
let r : ^t ref = ref LanguagePrimitives.GenericZero
let addv : ^t ref= ref LanguagePrimitives.GenericOne
while ((int) !r) < n do
if int(!r + !addv + !addv) < n then
addv := !addv + !addv
else
r := !r + !addv
addv := LanguagePrimitives.GenericOne
!r * m
Using some of the library only features could make this a little better, but would result in warnings.
Note: This solution assumes that n is representable in ^t - i.e.
MulInt 2uy 5000
will loop forever
let inline MultiplyByInt n (x: ^a) =
let zero : ^a = LanguagePrimitives.GenericZero
let one : ^a = LanguagePrimitives.GenericOne
if n = 0 then zero
else
let mutable q, r = System.Math.DivRem(abs n, 2)
let mutable y = x
while q > 0 do
y <- y + y
q <- q / 2
let y = if r = 0 then y else y + x
if n > 0 then y
else y * (zero - one)
I've received an answer from Don Syme (via fsbugs email) when I've asked about missing MutliplyByInt and limited support of DivideByInt:
Don's answer:
This operator exists to support “Seq.averageBy” etc. This represents pseudo-precise division of the total by the count. We didn’t extend the mechanism beyond what was needed for that.
So it looks like I've misunderstood the purpose of this mechanism.
In my quest to learn more F#, I tried to implement an "accumulator generator" as described by Paul Graham here. My best solution so far is completely dynamically typed:
open System
let acc (init:obj) : obj->obj=
let state = ref init
fun (x:obj) ->
if (!state).GetType() = typeof<Int32>
&& x.GetType() = typeof<Int32> then
state := (Convert.ToInt32(!state) + Convert.ToInt32(x)) :> obj
else
state := (Convert.ToDouble(!state) + Convert.ToDouble(x)) :> obj
!state
do
let x : obj -> obj = acc 1 // the type annotation is necessary here
(x 5) |> ignore
printfn "%A" (x 2) // prints "8"
printfn "%A" (x 2.3) // prints "10.3"
I have three questions:
If I remove the type annotation for x, the code fails to compile because the compiler infers type int -> obj for x - although acc is annotated to return an obj->obj. Why is that and can I avoid it?
Any ideas to improve this dynamically typed version?
Is it possible to implement this with proper static types? Maybe with member constraints? (It is possible in Haskell, but not in OCaml, AFAIK)
In my quest to learn more F#, I tried to implement an "accumulator generator" as described by Paul Graham here.
This problem requires the existence of an unspecified numeric tower. Lisp happens to have one and it happens to be adequate for Paul Graham's examples because this problem was specifically designed to make Lisp look artificially good.
You can implement a numeric tower in F# either using a union type (like type number = Int of int | Float of float) or by boxing everything. The following solution uses the latter approach:
let add (x: obj) (y: obj) =
match x, y with
| (:? int as m), (:? int as n) -> box(m+n)
| (:? int as n), (:? float as x)
| (:? float as x), (:? int as n) -> box(x + float n)
| (:? float as x), (:? float as y) -> box(x + y)
| _ -> failwith "Run-time type error"
let acc x =
let x = ref x
fun (y: obj) ->
x := add !x y
!x
let x : obj -> _ = acc(box 1)
do x(box 5)
do acc(box 3)
do printfn "%A" (x(box 2.3))
However, numeric towers are virtually useless in the real world. Unless you are very careful, trying to learn from these kinds of borked challenges will do you more harm than good. You should leave asking yourself why we do not want a numeric tower, do not want to box and do not want run-time type promotion?
Why didn't we just write:
let x = 1
let x = x + 5
ignore(3)
let x = float x + 2.3
We know the type of x at every step. Every number is stored unboxed. We know that this code will never produce a run-time type error...
I agree with Jon that this is quite artificial example and it is not a good starting point for learning F#. However, you can use static member constraints to get reasonably close without dynamic casts and reflection. If you mark it as inline and add convert both of the parameters using float:
let inline acc x =
let x = ref (float x)
fun y ->
x := (float y) + !x
!x
You'll get a function with the following type:
val inline acc :
^a -> ( ^b -> float)
when ^a : (static member op_Explicit : ^a -> float) and
^b : (static member op_Explicit : ^b -> float)
The function takes any two arguments that can be explicitly converted to float. The only limitation compared to the LISP version (I guess) is that it always returns float (as the most universal numeric type available). You can write something like:
> acc 1 2;; // For two integers, it returns float
val it : float = 3.0
> acc 1 2.1;; // integer + float
val it : float = 3.1
> acc 1 "31";; // It even works with strings!
val it : float = 32.0
It's definitely not possible to implement this with proper static types. You say you can in Haskell, but I don't believe you.
The problem with trying to do this with static typing is in adding two different numbers of possibly different types while preserving the type of the left-hand side. As Jon Harrop says this is possible with a union type. Once you've defined the union type and a corresponding addition operation which works as mentioned, the actual accumulator is very simple. My implementation:
module MyTest
type Numeric =
| NInt of int
| NFloat of float
member this.Add(other : Numeric) : Numeric =
match this with
| NInt x ->
match other with
| NInt y -> NInt (x + y)
| NFloat y -> NInt (x + (int y))
| NFloat x ->
match other with
| NInt y -> NFloat (x + (float y))
| NFloat y -> NFloat (x + y)
override this.ToString() =
match this with
| NInt x -> x.ToString()
| NFloat x -> x.ToString()
let foo (n : Numeric) =
let acc = ref n
fun i ->
acc := (!acc).Add(i)
!acc
let f = foo (NFloat 1.1)
(2 |> NInt |> f).ToString() |> printfn "%s"