In an Fsharp application, I have defined several union case types as
type A = A of String
type B = B of String
type C = C of String
I would like to define a function to extract the value from the union case instances.
let getValue( ctor: String-> 'a) =
...implementation here
Is there anyway to achieve such task?
Thanks.
Let's say you have:
type A = A of string
type B = B of string
type C = C of string
let a = A "hello"
let b = B "world"
let c = C "!"
There are many ways to extract those values, here are some:
Individual unwrappers
let getValueA (A v) = v
let getValueB (B v) = v
let getValueC (C v) = v
let valueOfA = getValueA a
let valueOfB = getValueB b
let valueOfC = getValueC c
Method overload
type T =
static member getValue (A v) = v
static member getValue (B v) = v
static member getValue (C v) = v
let valueOfA = T.getValue a
let valueOfB = T.getValue b
let valueOfC = T.getValue c
Function overload
type GetValue = GetValue with
static member ($) (GetValue, (A v)) = v
static member ($) (GetValue, (B v)) = v
static member ($) (GetValue, (C v)) = v
let inline getValue x : string = GetValue $ x
let valueOfA = getValue a
let valueOfB = getValue b
let valueOfC = getValue c
Reflection
open Microsoft.FSharp.Reflection
let getValue a =
FSharpValue.GetUnionFields (a, a.GetType())
|> snd
|> Seq.head
:?> string
let valueOfA = getValue a
let valueOfB = getValue b
let valueOfC = getValue c
Redesign your DU
type A = A
type B = B
type C = C
type MyDU<'a> = MyDU of 'a * string
let a = MyDU (A, "hello")
let b = MyDU (B, "world")
let c = MyDU (C, "!" )
let getValue (MyDU (_, v)) = v
let valueOfA = getValue a
let valueOfB = getValue b
Redesign with interfaces
type IWrapped<'a> =
abstract getValue: 'a
type A = A of string with interface IWrapped<string> with member t.getValue = let (A x) = t in x
type B = B of string with interface IWrapped<string> with member t.getValue = let (B x) = t in x
type C = C of string with interface IWrapped<string> with member t.getValue = let (C x) = t in x
let valueOfA = (a :> IWrapped<string>).getValue
Related
Consider a generic container like:
type Foo<'t> =
{
Foo : 't
}
This generic function works OK:
module Foo =
let inline emptyFoo () =
{
Foo = LanguagePrimitives.GenericZero
}
But this value does not:
module Foo =
let emptyFoo =
{
Foo = LanguagePrimitives.GenericZero
}
This is because the compiler infers emptyFoo to have type Foo<obj>.
However, the standard library has generic values like List.empty, so how is that achieved there?
You have to make it explicitly generic.
List.empty is implemented like this:
let empty<'T> = ([ ] : 'T list)
You could implement a List by yourself, with an empty. It looks like this.
type L<'a> =
| Null
| Cons of 'a * L<'a>
module L =
let empty = Null
let xs = Cons(1, Cons(2, L.empty))
let ys = Cons(1.0,Cons(2.0,L.empty))
So, why does L.empty in this case works in a generic way? Because the value Null has no special value attached to it. You could say, it is compatible with every other generic.
In your Record on the other hand, you always must produce a value. LanguagePrimitive.GenericZero is not some Generic value. It help so to resolve to a special zero value, and this value is determined by the other code you write.
For example
let x = LanguagePrimitives.GenericZero
is also obj
let x = LanguagePrimitives.GenericZero + 1
will be int. And
let x = LanguagePrimitives.GenericZero + 1.0
will be float. So in some case you can think of GenericZero just as a placeholder for zero for the special type you need, but the code needs to determine at this point, which type you want.
You could change your type, with an option to provide a real empty.
type Foo<'t> = {
Foo: 't option
}
module Foo =
let empty = { Foo = None }
let x = Foo.empty // Foo<'a>
let y = { x with Foo = Some 1 } // Foo<int>
let z = { x with Foo = Some 1.0 } // Foo<float>
Zero Member
Maybe you want a Zero Member on some types. For example
type Vector3 = {X:float; Y:float; Z:float} with
static member create x y z = {X=x; Y=y; Z=z}
static member (+) (a,b) = Vector3.create (a.X + b.X) (a.Y + b.Y) (a.Z + b.Z)
static member Zero = Vector3.create 0.0 0.0 0.0
static member DivideByInt(a,b) =
Vector3.create
(LanguagePrimitives.DivideByInt a.X b)
(LanguagePrimitives.DivideByInt a.Y b)
(LanguagePrimitives.DivideByInt a.Z b)
then you can write
let xs = [1;2;3]
let ys = [Vector3.create 1.0 1.0 1.0; Vector3.create 1.0 1.0 1.0]
let inline sum xs =
List.fold (fun a b -> a + b) LanguagePrimitives.GenericZero xs
let sumx = sum xs // int: 6
let sumy = sum ys // Vector3: 2.0 2.0 2.0
Given the following types
type IDocPartX<'delta, 'pures> =
abstract member ToDelta: 'pures -> 'delta
abstract member ToPure: 'delta -> 'pures
abstract member Validate: 'pures -> Option<'pures>
type InitX<'a>(v:'a) =
member this.Value = v
type Foo<'a> = {value: 'a} with
interface IDocPartX<Foo<int>, Foo<string>> with
member this.ToDelta x = Unchecked.defaultof<_>
member this.ToPure x = Unchecked.defaultof<_>
member this.Validate x = Unchecked.defaultof<_>
this function
let inline ValidateInitX(x:InitX<IDocPartX<'d,'p>>) =
let r = x.Value
let d = r :?> 'd
let o =
d
|> r.ToPure
|> r.Validate
match o with
| Some v -> r.ToDelta v |> Init |> Some
| _ -> None
and this values
let a = InitX {value = 1}
let b = ValidateInitX a
why is the value a not recognized to be of InitX<IDocPartX<'a, 'b>>?
OK, a bit of SRTPs magic on the function definition will do the trick
let inline ValidateInitX<'d, 'p when 'd :> IDocPartX<'d,'p>>(x:InitX<'d>) =
let r = x.Value
let o =
r
|> r.ToPure
|> r.Validate
match o with
| Some v -> r.ToDelta v |> Init |> Some
| _ -> None
I'm trying to write a computational workflow which would allow a computation which can produce side effects like log or sleep and a return value
A usage example would be something like this
let add x y =
compute {
do! log (sprintf "add: x = %d, y= %d" x y)
do! sleep 1000
let r = x + y
do! log (sprintf "add: result= %d" r)
return r
}
...
let result = run (add 100 1000)
and I would like the side effects to be produced when executeComputation is called.
My attempt is
type Effect =
| Log of string
| Sleep of int
type Computation<'t> = Computation of Lazy<'t * Effect list>
let private bind (u : 'u, effs : Effect list)
(f : 'u -> 'v * Effect list)
: ('v * Effect list) =
let v, newEffs = f u
let allEffects = List.append effs newEffs
v, allEffects
type ComputeBuilder() =
member this.Zero() = lazy ((), [])
member this.Return(x) = x, []
member this.ReturnFrom(Computation f) = f.Force()
member this.Bind(x, f) = bind x f
member this.Delay(funcToDelay) = funcToDelay
member this.Run(funcToRun) = Computation (lazy funcToRun())
let compute = new ComputeBuilder()
let log msg = (), [Log msg]
let sleep ms = (), [Sleep ms]
let run (Computation x) = x.Force()
...but the compiler complains about the let! lines in the following code:
let x =
compute {
let! a = add 10 20
let! b = add 11 2000
return a + b
}
Error FS0001: This expression was expected to have type
'a * Effect list
but here has type
Computation<'b> (FS0001)
Any suggestions?
The main thing that is not right with your definition is that some of the members of the computation builder use your Computation<'T> type and some of the other members use directly a pair of value and list of effects.
To make it type check, you need to be consistent. The following version uses Computation<'T> everywhere - have a look at the type signature of Bind, for example:
let private bind (Computation c) f : Computation<_> =
Computation(Lazy.Create(fun () ->
let u, effs = c.Value
let (Computation(c2)) = f u
let v, newEffs = c2.Value
let allEffects = List.append effs newEffs
v, allEffects))
type ComputeBuilder() =
member this.Zero() = Computation(lazy ((), []))
member this.Return(x) = Computation(lazy (x, []))
member this.ReturnFrom(c) = c
member this.Bind(x, f) = bind x f
member this.Delay(funcToDelay:_ -> Computation<_>) =
Computation(Lazy.Create(fun () ->
let (Computation(r)) = funcToDelay()
r.Value))
is it possible to have the same operator in a type definition work with different types on the right-hand side of the operator?
Definition of the |== operator via name plugElts compiles fine, but using it later in module RemoverTest fails with error FS0002: This function takes too many arguments, or is used in a context where a function is not expected when function is supplied on the right-hand side.
module SampleOperators =
let inline (|==) (x: ^URel) (wires: ^Wires) =
(^URel: (static member plugElts: ^URel * ^Wires -> 'R) (x, wires))
module Remover =
open SampleOperators
type RemGroup<'G> = RemGroupPass | RemGroupAll | RemGroupExcept of 'G | RemGroupElts of 'G
type RemMap<'K,'P when 'K: comparison> =
RemGroup<Map<'K,'P>>
type RemFun<'K,'P when 'K: comparison> =
'K * 'P -> bool
type Rem<'K,'MapVal,'FunVal when 'K:comparison> =
| Map_ of RemMap<'K,'MapVal>
| Fun_ of RemFun<'K,'FunVal>
type X<'K,'P when 'K:comparison> =
{ id: 'K
vv: Rem<'K,'P,'P> }
static member inline plugElts (x:X<_,_>, ws:('K * 'P) seq) =
{x with vv = Map_ (RemGroupElts (Map.ofSeq ws))}
static member inline plugElts (x:X<_,_>, i:int) =
{x with vv = Map_ (RemGroupElts (Map.ofSeq [i,i]))}
static member inline plugElts (x:X<_,_>, fn:('K * 'P -> bool)) =
{x with vv = Fun_ fn}
module RemoverTest =
open Remover
open SampleOperators
let xxx1 () =
{id = 1; vv = Map_ RemGroupPass} |== [1,1] // compiles ok
let xxx2 () =
{id = 1; vv = Map_ RemGroupPass} |== 1 // compiles ok
let xxx3 () =
({id = 1; vv = Map_ RemGroupPass}:X<_,_>) |== (fun _ -> bool) // can't compile
Are there ways to make this work without wrapping the right-hand side in discriminated union?
Thanks,
Karol
edit: added overload for right-hand side with int type which works fine
The moment you invoke the operator in xxx3 you need to provide an actual function - what you have there right now is only the equivalent of a type declaration. Change to the following, and it will compile:
let xxx3 () =
({id = 1; vv = Map_ RemGroupPass}:X<_,_>) |== (fun _ -> true)
Here's a more compact version of your question:
type MoreFun<'T> = 'T -> int
type X<'T> =
{
B: int
F: MoreFun<'T>
}
static member (|==) (a: X<_>, b: int) = { B = b; F = fun (f:int) -> b}
static member (|==) (a: X<_>, b: MoreFun<_>) = { a with F = b }
module Tests =
let one (f: int) = 1
let t1() = { B = 1; F = one } |== 2
// let t2() = { B = 1; F = one } |== (fun _ -> int) // Does not work
let three: MoreFun<_> = (fun _ -> 3)
let t3() = { B = 1; F = one } |== three
// You don't need to cast three to be of type MoreFun:
let t4() = { B = 1; F = one } |== (fun _ -> 3)
in F#, How to make a function wrapper (x:'T) to wrap any inputs to an array of array, say: wrapper(1) = [|[|1|]|]; wrapper([|1|]) = [|[|1|]|]; and wrapper([|[|1|]|]) = [|[|1|]|]; something like below does not work:
let wrapper (x:'T) =
let y =
if not <| x.GetType().IsArray then [|[|x|]|]
elif not <| x.[0].GetType().IsArray then [|x|]
else x
y
The following seems to work:
let wrapper<'a, 'b> (x:'a) =
match box x with
| null -> null
| :? array<array<'b>> as y -> y
| :? array<'b> as y -> [|y|]
| y -> [|[|unbox y|]|]
The signature is 'a -> array<array<'b>>.
In response to your comment: this sort of thing can be done in statically-typed languages--and, arguably better than in a dynamic language--but/because stepping outside the type system must be explicit (e.g., box/unbox).
Here's a reflection based solution which will accept inputs of arbitrary nested array depth:
open System.Text.RegularExpressions
let wrapper input =
let ty = input.GetType()
if ty.IsArray |> not then
[|[|input |> unbox|]|]
else
let depth = Regex.Matches(ty.Name, "\[\]", RegexOptions.Compiled).Count
let rec findInnerItem curDepth curArray =
let innerItem = curArray.GetType().GetMethod("Get").Invoke(curArray, [|box 0|])
if curDepth = depth then
innerItem
else
findInnerItem (curDepth+1) innerItem
let innerItem = findInnerItem 1 input
[|[|innerItem |> unbox|]|]
Usage in FSI:
val wrapper : 'a -> 'b [] []
> let x : int[][] = wrapper 1;;
val x : int [] [] = [|[|1|]|]
> let x : int[][] = wrapper [|1|];;
val x : int [] [] = [|[|1|]|]
> let x : int[][] = wrapper [|[|1|]|];;
val x : int [] [] = [|[|1|]|]
> let x : int[][] = wrapper [|[|[|1|]|]|];;
val x : int [] [] = [|[|1|]|]
> let x : int[][] = wrapper [|[|[|[|1|]|]|]|];;
val x : int [] [] = [|[|1|]|]
> let x : int[][] = wrapper [|[|[|[|[|1|]|]|]|]|];;
val x : int [] [] = [|[|1|]|]
> let x : int[][] = wrapper [|[|[|[|[|[|1|]|]|]|]|]|];;
val x : int [] [] = [|[|1|]|]
You can't, that's not a well-typed function (try to write out the type signature).
You function needs to "wrap any inputs to an array of array". As par this statement the solution is as simple as:
let wrapper (x:'T) = [|[|x|]|];
But then the examples you have given are not as par your function definition.
i.e wrapper([|1|]) = [|[|1|]|] should be wrapper([|1|]) = [|[|[|1|]|]|] as par your function definition.