Common parameter in an Union - f#

Hi FSharpers (or any MLer)
Still trying to improve my Domain modeling skills (coming from OOP). Let's say that I have the following
type A =
| AA of AA
| AB of AB
type AA =
{ Code : 'a
Time : 'b }
type AB =
{ Code : 'a
Whatsoever : 'c }
Let's now imagine that I want the following function signature : (A -> 'a)
To my current understanding, the solution I have is to do:
let f (a1:A) =
match a1 with
| AA a -> a.Code
| AB a -> a.Code
the inconvenient with this solution is that it requires me to always add new cases to my match if I were to add some to the A union.
I imagine another solution would be a tuple-type solution (but losing the "naming" of the field):
type A =
| AA of AA * 'a
| AB of AB * 'a
type AA =
{ Time : 'b }
type AB =
{ Whatsoever : 'c }
let f (a1:A) =
snd a1
Is there any easy solution that I have missed?
Thanks!

This:
let f (a1:A) = snd a1
would not work because to get to the tuple you first need to match AA and AB.
This could work:
type A0<'b, 'c> =
| AA of AA<'b>
| AB of AB<'c>
type A<'a, 'b, 'c> = A0<'b, 'c> * 'a
let f (a1:A<_,_,_>) =
snd a1
Your solution with Tmp<_> is equivalent to using a tuple.
BTW in F# the order of elements is important, in order to access type AA it needs be declared before type A. Your first example should look something like this:
type AA<'a, 'b> = {
Code : 'a
Time : 'b }
type AB<'a, 'c> = {
Code : 'a
Whatsoever : 'c }
type A<'a, 'b, 'c> =
| AA of AA<'a, 'b>
| AB of AB<'a, 'c>
let f (a1:A<_,_,_>) =
match a1 with
| AA a -> a.Code
Also to use generic parameters like 'a or 'b they need to be declared at the top:
type A<'a, 'b, 'c> = ...

I might have an idea, using a generic type such as:
type A =
| AA of Tmp<AA>
| AB of Tmp<AB>
type Tmp<'T> =
{ Code : 'a
Other : 'T }
type AA =
{ Time : 'b }
type AB =
{ Whatsoever : 'c }

Related

Generic pattern matching in function argument

Let say I have DU of three types and a function which accepts this DU as a parameter:
type A = decimal<p>
type B = decimal<p>
type C = decimal<p>
type ABC = A of A | B of B | C of C
let myfunc (val: ABC) =
match val with
| A v -> ...
| B v -> ...
| C v -> ...
Is there a better way to define a function which accepts DU of only A and B without defining new type explicity?
This is a working example:
type AB = A2 of A | B2 of B
let myfunc2 (val: AB) =
match val with
| A2 v -> ...
| B2 v -> ...
This is how I would like it to be:
let myfunc2 (val: A|B) =
match val with
| A v -> ...
| B v -> ...
The language feature you're asking for is open variants, which exists in OCaml, but not in F#. For instance,
type ABC = A of int | B of int | C of int
type AB = A of int | B of int
let myfunc (x: AB) = ...
is legal in OCaml. This can also be achieved with type-erased DUs (vote for it here!).
DU cases in F# are not actual types, but the base type (ABC) with a tag denoting which union case it is. And since there are no types which exist separately, it follows that they cannot be used as a type constraint.
You can however try and work around this with Choice. The idea is to build single case DUs and then combine them with Choice<T1, T2...>.
type A = A of int
type B = B of int
type C = C of int
type ABC = Choice<A, B, C>
type AB = Choice<A, B>
let matchAb (x: AB) =
match x with
| Choice1Of2 (A a) -> a
| Choice2Of2 (B b) -> b

How to convert generic Discriminated Union to obj

Given a DU like
type Result<'a, 'b> = Ok of 'a | Error of 'b
and some functions
let doA () = Ok true
let doB () = Error <| exn "Fail"
let doC = function | 1 -> Ok "one" | x -> Error x
How do you define a function to cast the value?
toObjResult : x:obj -> Result<obj, obj> //where x is guaranteed to be Result<'a,'b>
Usage
let data =
[ doA() |> box
doB() |> box
docC 1 |> box
docC 2 |> box ]
|> List.map toObjResult
All attempts so far restrict the types of 'a and 'b to be obj
let toObjResult (x:obj) =
match x with
| :? Result<'a, 'b> as r ->
match r with
| Ok a -> Ok (box a)
| Error b -> Error (box b)
| _ -> Error <| (exn "Invalid type" |> box)
resulting in errors like
System.InvalidCastException: Unable to cast object of type 'Ok[System.Boolean,System.Object]' to type 'Result`2[System.Object,System.Object]'.
There is no way to do this without using reflection, enumerating all types, or modifying the type.
Using reflection can be slow, but lets you do what you want (see [the GenericType active pattern from this answer) and the answer from #robkuz shows how you can do this by listing all the cases that you want to cover - the problem is that this does not scale well.
Finally, if you were happy to modify your Result<'a, 'b> type, you could add a non-generic interface that lets you get the value as a boxed value:
type IBoxedResult =
abstract Boxed : Result<obj, obj>
and Result<'a, 'b> =
| Ok of 'a
| Error of 'b
interface IBoxedResult with
member x.Boxed =
match x with
| Ok v -> Ok (box v)
| Error v -> Error (box v)
Now you can cast obj to IBoxedResult and use Boxed to get the value as Reslt<obj, obj>:
[ box (Ok true)
box (Ok 1) ]
|> List.map (fun o -> (o :?> IBoxedResult).Boxed)
You have to match on the exact generic type params of your Result type in your matching expression
let matchR r =
match r with
| Ok a -> Ok (box a)
| Error b -> Error (box b)
let toObjResult (x:obj) =
match x with
| :? Result<bool, _> as r -> matchR r
| :? Result<string, int> as r -> matchR r
| :? Result<_, Exception> as r -> matchR r
| _ -> Error (box "Invalid type" )
sadly you can't match on unrealised type params (which is really bad)

How do sequence expressions and polymorphic recursion play together?

This project really is a source of questions for me.
I already learned about polymorphic recursion and I understand why it is a special case and therefore F# needs full type annotations.
For regular functions I might need some fiddeling but usually get it right. Now I'm trying to adapt a (working) basic toSeq to a more specialized finger tree, but can't.
My feeling is that the use of the computation expression has something to do with it. This is the condensed working version:
module ThisWorks =
module Node =
type Node<'a> =
| Node2 of 'a * 'a
| Node3 of 'a * 'a * 'a
let toList = function
| Node2(a, b) -> [a; b]
| Node3(a, b, c) -> [a; b; c]
module Digit =
type Digit<'a> =
| One of 'a
| Two of 'a * 'a
| Three of 'a * 'a * 'a
| Four of 'a * 'a * 'a * 'a
let toList = function
| One a -> [a]
| Two(a, b) -> [a; b]
| Three(a, b, c) -> [a; b; c]
| Four(a, b, c, d) -> [a; b; c; d]
module FingerTree =
open Node
open Digit
type FingerTree<'a> =
| Empty
| Single of 'a
| Deep of Digit<'a> * Lazy<FingerTree<Node<'a>>> * Digit<'a>
let rec toSeq<'a> (tree:FingerTree<'a>) : seq<'a> = seq {
match tree with
| Single single ->
yield single
| Deep(prefix, Lazy deeper, suffix) ->
yield! prefix |> Digit.toList
yield! deeper |> toSeq |> Seq.collect Node.toList
yield! suffix |> Digit.toList
| Empty -> ()
}
The one I don't manage to get to compile is this:
module ThisDoesnt =
module Monoids =
type IMonoid<'m> =
abstract Zero:'m
abstract Plus:'m -> 'm
type IMeasured<'m when 'm :> IMonoid<'m>> =
abstract Measure:'m
type Size(value) =
new() = Size 0
member __.Value = value
interface IMonoid<Size> with
member __.Zero = Size()
member __.Plus rhs = Size(value + rhs.Value)
type Value<'a> =
| Value of 'a
interface IMeasured<Size> with
member __.Measure = Size 1
open Monoids
module Node =
type Node<'m, 'a when 'm :> IMonoid<'m>> =
| Node2 of 'm * 'a * 'a
| Node3 of 'm * 'a * 'a * 'a
let toList = function
| Node2(_, a, b) -> [a; b]
| Node3(_, a, b, c) -> [a; b; c]
module Digit =
type Digit<'m, 'a when 'm :> IMonoid<'m>> =
| One of 'a
| Two of 'a * 'a
| Three of 'a * 'a * 'a
| Four of 'a * 'a * 'a * 'a
let toList = function
| One a -> [a]
| Two(a, b) -> [a; b]
| Three(a, b, c) -> [a; b; c]
| Four(a, b, c, d) -> [a; b; c; d]
module FingerTree =
open Node
open Digit
type FingerTree<'m, 'a when 'm :> IMonoid<'m>> =
| Empty
| Single of 'a
| Deep of 'm * Digit<'m, 'a> * Lazy<FingerTree<'m, Node<'m, 'a>>> * Digit<'m, 'a>
let unpack (Value v) = v
let rec toSeq<'a> (tree:FingerTree<Size, Value<'a>>) : seq<'a> = seq {
match tree with
| Single(Value single) ->
yield single
| Deep(_, prefix, Lazy deeper, suffix) ->
yield! prefix |> Digit.toList |> List.map unpack
#if ITERATE
for (Value deep) in toSeq deeper do
^^^^^
yield deep
#else
yield! deeper |> toSeq |> Seq.collect (Node.toList >> List.map unpack)
^^^^^
#endif
yield! suffix |> Digit.toList |> List.map unpack
| Empty -> ()
}
The error message I get says
Error Type mismatch. Expecting a
FingerTree<Size,Node<Size,Value<'a>>> -> 'b
but given a
FingerTree<Size,Value<'c>> -> seq<'c>
The type 'Node<Size,Value<'a>>' does not match the type 'Value<'b>'
and the squiggles underline the recursive call of toSeq.
I know that the “deeper” type is encapsulated in a Node and in the working code I just unpack it afterwards. But here the compiler trips already before I get the chance to unpack. Trying a for (Value deep) in toSeq deeper do yield deep has the same problem.
I already have a way out, namely to use the toSeq of the “base” Tree and Seq.map unpack afterwards. Not true, trying that yields a very similar error message.
I'm curious what makes this code break and how it could be fixed.
The compiler's error message seems clear to me: toSeq is applicable only to values of type FingerTree<Size, Value<'a>> for some 'a, but you're trying to call it on a value of type FingerTree<Size,Node<Size,Value<'a>>> instead, which is not compatible. There's nothing specific to polymorphic recursion or sequence expressions, these types just don't match.
Instead, it seems like it would be much simpler to make toSeq more generic by taking an input of type FingerTree<Size, 'a> (without any reference to Value), which would enable the recursive call you want. Then you can easily derive the more specific function you actually want by composing the more general toSeq with Seq.map unpack.

Pattern match on a list of tuples

If I want to add all the elements of a list of tuples, I get an error with the following
let rec addTupLst (xs: 'a * 'a list) =
match xs with
| (a, b) :: rst -> a + b + (addTupLst rst)
| _ -> 0
addTupLst [(1, 2)]
I get the warning
error FS0001: This expression was expected to have type
'a * 'a list
but here has type
'b list
Is it not possible to pattern match on a list of tuples this way, or is there another error?
You just forgot a pair of parens
let rec addTupLst (xs: ('a * 'a) list) =
match xs with
| (a, b) :: rst -> a + b + (addTupLst rst)
| _ -> 0
addTupLst [(1, 2)]
The problem is that you declare the function as taking a 'a * 'a list, but what you actually want to write is ('a * 'a) list.
This is one of the reasons why I don't really like the common but (IMO) inconsistent style of using prefix notation for type parameters for some built-in types and postfix notation for the rest. I prefer to write the type as list<'a * 'a>.

Using F# List.sort with a type that has CustomComparison

I am trying to define a type in F# that has an id and a value, and when sorting only the value is considered. I have simplified the situation to make a hopefully clear example.
I have the following so far:
[<CustomEquality; CustomComparison>]
type EquatableValue<'T when 'T : comparison> =
{ id : string; value : 'T }
override x.Equals(yobj) =
match yobj with
| :? EquatableValue<'T> as y ->
x.value = y.value
| _ -> false
override x.GetHashCode() =
hash x.value
interface System.IComparable with
member x.CompareTo yobj =
match yobj with
| :? EquatableValue<'T> as y ->
compare x.value y.value
| _ -> invalidArg "yobj" "cannot compare values of different types"
let a = {id="a";value=5}
let b = {id="b";value=4}
let c = {id="c";value=7}
let d = {id="d";value=1}
let x = [a,b,c,d]
let sorted = x |> List.sort
I would expect the elements of sorted to be in the order (by id) d, b, c, a.
But this results in the order a, b, c, d.
Can anyone help me understand what I am doing wrong?
Many thanks.
your list contains only one element - tuple consisting of 4 values:
let x = [a,b,c,d]
use
let x = [a;b;c;d]
because elements of list are divided by ';'

Resources