Understanding inference of discriminated unions - f#

Consider the following code...
type TypeOne () =
member val Name = "" with get, set
type TypeTwo () =
member val Name = "" with get, set
member val Property = 0 with get, set
[<RequireQualifiedAccess>]
type UnionType =
| TypeOne of TypeOne
| TypeTwo of TypeTwo
// this only succeeds because we have defined the union type a
// requiring qualified access. otherwise TypeOne would be inferred
// as the union case, not the type (understandably)...
let t1 = TypeOne ()
// if we want t1 "as" the union type, we have to do the following...
let ut1 = UnionType.TypeOne t1
// the following function returns a result of type UnionType
// but we have to explicitly do this for each type
let makeUnion switch =
match switch with
| true -> UnionType.TypeOne (TypeOne ())
| _ -> UnionType.TypeTwo (TypeTwo ())
As in the comments, it seems like there's no way of inferring that return results should be of a union type, and if we have to require qualified access on the union type this is extremely verbose (which seems worryingly wrong).
There's also no way of creating a function that takes only the union types and returns them as the union. (In this case a function which takes a TypeOne or TypeTwo and returns UnionType). Or is there? Are there better ways of working with discriminated unions?

As pad has pointed out - you don't need to qualify a union case with its parent type - so you can write
let makeUnion switch =
match switch with
| true -> TypeOne (new TypeOne ())
| false -> TypeTwo (new TypeTwo ())
However you will always have to specify the TypeOne. This is to avoid the ambiguity present if you have multiple cases which take the same arguments - like
type foo=
|Bar of int
|Baz of int
Then even knowing the return type, the compiler can't work out what to return. A more common example is actually with cases which take no arguments - say you decide to redifine true and false:
type bool = |True |False

type TypeOne() =
member val Name = "" with get, set
type TypeTwo() =
member val Name = "" with get, set
member val Property = 0 with get, set
type UnionType =
| TypeOne of TypeOne
| TypeTwo of TypeTwo
/// You can give some hints to the type checker
/// by saying that you want a class constructor
let t1 = new TypeOne()
/// No need to use fully-qualified access on union types
let ut1 = TypeOne t1
let makeUnion switch =
match switch with
| true -> TypeOne (new TypeOne())
| false -> TypeTwo (new TypeTwo())

Related

Casting a system.Object to a specific type in F#

I have a standard type that I use to pass messages and objects between functions that has an optional MessageObject that is System.Object.
type StatusMessage = {
Message: string
MessageObject: Object option
Success: bool
}
In the following specific function - I know that the Message Object will always contain the following specific type and I want to be able to access that within the function
type SingleCorrectRecord = {
CardTransactionWithOrder: CardWithOrder
Line: RowTransaction
ExchangeVariance: decimal
}
My function is:
let getEstimatedVariance(matchedTransactions: StatusMessage list): decimal =
let exchangeVariance:decimal =
matchedTransactions |> Seq.sumBy(fun mt -> mt.MessageObject.ExchangeVariance)
estimatedVariance
Obviously mt.MessageObject doesn't contain the property "ExchangeVariance" but I need to be able to cast (or unbox?) the object so that it knows that it is a SingleCorrectRecord type and I can access the properties.
Any help is appreciated.
You can nest the dynamic type test with the union case pattern in the same match expression:
let exchangeVariance =
matchedTransactions |> Seq.sumBy (fun mt ->
match mt.MessageObject with
| Some(:? SingleCorrectRecord as scr) -> scr.ExchangeVariance
| _ -> 0M )
// val exchangeVariance : decimal
:?> is the downcast operator (or :? within pattern matching) which can be used for this problem. However, it is typically not recommended to use downcasting, which can fail at runtime, in F# code.
As an alternative, you could structure your StatusMessage type to be generic or to use a discriminated union depending on whether messages with different payload types need to be stored in the same collection.
// This type can store any type of message however StatusMessage<SingleCorrectRecord> is
// a different type to StatusMessage<Other> and they can't be stored in the same collection.
type StatusMessage<'T> = {
Message: string
MessageObject: 'T option
Success: bool
}
let getEstimatedVariance(matchedTransactions: StatusMessage<SingleCorrectRecord> list): decimal =
let estimatedVariance:decimal =
matchedTransactions |> Seq.sumBy(fun mt ->
match mt.MessageObject with
| Some msg -> msg.ExchangeVariance
| None -> 0M )
estimatedVariance
If the messages need to be in a grouped collection you could define all messages in a MsgPayload discriminated union:
type MsgPayload =
| SingleCorrectRecord of SingleCorrectRecord
| Other of string
type StatusMessage2 = {
Message: string
MessageObject: MsgPayload option
Success: bool
}
let getEstimatedVariance2(matchedTransactions: StatusMessage2 list): decimal =
let estimatedVariance:decimal =
matchedTransactions |> Seq.sumBy(fun mt ->
match mt.MessageObject with
| Some (SingleCorrectRecord msg) -> msg.ExchangeVariance
| _ -> 0M )
estimatedVariance

Discriminated Union - Allow Pattern Matching but Restrict Construction

I have an F# Discriminated Union, where I want to apply some "constructor logic" to any values used in constructing the union cases. Let's say the union looks like this:
type ValidValue =
| ValidInt of int
| ValidString of string
// other cases, etc.
Now, I want to apply some logic to the values that are actually passed-in to ensure that they are valid. In order to make sure I don't end up dealing with ValidValue instances that aren't really valid (haven't been constructed using the validation logic), I make the constructors private and expose a public function that enforces my logic to construct them.
type ValidValue =
private
| ValidInt of int
| ValidString of string
module ValidValue =
let createInt value =
if value > 0 // Here's some validation logic
then Ok <| ValidInt value
else Error "Integer values must be positive"
let createString value =
if value |> String.length > 0 // More validation logic
then Ok <| ValidString value
else Error "String values must not be empty"
This works, allowing me to enforce the validation logic and make sure every instance of ValidValue really is valid. However, the problem is that no one outside of this module can pattern-match on ValidValue to inspect the result, limiting the usefulness of the Discriminated Union.
I would like to allow outside users to still pattern-match and work with the ValidValue like any other DU, but that's not possible if it has a private constructor. The only solution I can think of would be to wrap each value inside the DU in a single-case union type with a private constructor, and leave the actual ValidValue constructors public. This would expose the cases to the outside, allowing them to be matched against, but still mostly-prevent the outside caller from constructing them, because the values required to instantiate each case would have private constructors:
type VInt = private VInt of int
type VString = private VString of string
type ValidValue =
| ValidInt of VInt
| ValidString of VString
module ValidValue =
let createInt value =
if value > 0 // Here's some validation logic
then Ok <| ValidInt (VInt value)
else Error "Integer values must be positive"
let createString value =
if value |> String.length > 0 // More validation logic
then Ok <| ValidString (VString value)
else Error "String values must not be empty"
Now the caller can match against the cases of ValidValue, but they can't read the actual integer and string values inside the union cases, because they're wrapped in types that have private constructors. This can be fixed with value functions for each type:
module VInt =
let value (VInt i) = i
module VString =
let value (VString s) = s
Unfortunately, now the burden on the caller is increased:
// Example Caller
let result = ValidValue.createInt 3
match result with
| Ok validValue ->
match validValue with
| ValidInt vi ->
let i = vi |> VInt.value // Caller always needs this extra line
printfn "Int: %d" i
| ValidString vs ->
let s = vs |> VString.value // Can't use the value directly
printfn "String: %s" s
| Error error ->
printfn "Invalid: %s" error
Is there a better way to enforce the execution of the constructor logic I wanted at the beginning, without increasing the burden somewhere else down the line?
You can have private case constructors but expose public active patterns with the same names. Here's how you would define and use them (creation functions omitted for brevity):
module Helpers =
type ValidValue =
private
| ValidInt of int
| ValidString of string
let (|ValidInt|ValidString|) = function
| ValidValue.ValidInt i -> ValidInt i
| ValidValue.ValidString s -> ValidString s
module Usage =
open Helpers
let validValueToString = function
| ValidInt i -> string i
| ValidString s -> s
// 😎 Easy to use ✔
// Let's try to make our own ValidInt 🤔
ValidInt -1
// error FS1093: The union cases or fields of the type
// 'ValidValue' are not accessible from this code location
// 🤬 Blocked by the compiler ✔
Unless there's a particular reason that a discriminated union is required, given the particular use case you've provided it sounds like you don't actually want a discriminated union at all since an active pattern would be more useful. For example:
let (|ValidInt|ValidString|Invalid|) (value:obj) =
match value with
| :? int as x -> if x > 0 then ValidInt x else Invalid
| :? string as x -> if x.Length > 0 then ValidString x else Invalid
| _ -> Invalid
At that point, callers can match and be assured that the logic has been applied.
match someValue with
| ValidInt x -> // ...
| _ -> // ...

Construct generic function

I have a type:
type DictionaryCache<'a, 'b when 'a :comparison>()
And I have another type which contains some of this DictionaryCache:
type Cache() =
let user = new DictionaryCache<int, User>()
let userByLogin = new DictionaryCache<string, User>()
member this.User = user
member this.UserByLogin = userByLogin
In the last type I want to create generic function which will return one of the members based on input parameter:
member this.CacheNameToDictionary (cacheName: string) : DictionaryCache<'a, 'b> option =
match cacheName with
| "userByAutoincrementedId" -> Some(this.User)
| "userByLogin" -> Some(this.UserByLogin)
| _ -> None
But it doesn't work because of type mismatch.
Is there any way to rewrite this function ?
Update: here is a full code what I need to do:
type Cache() =
let user = new DictionaryCache<int, User>()
let userByLogin = new DictionaryCache<string, User>()
static let mutable instance = lazy(new Cache())
static member Instance with get() = instance.Value
member this.User = user
member this.UserByLogin = userByLogin
member this.Get (useCache: string) (cacheName: string) (id: 'a) longFunction exceptionFunction : 'b option =
let nameToDictionary() : DictionaryCache<'a, 'b> option =
match cacheName with
| "userByAutoincrementedId" -> Some(this.User)
| "userByLogin" -> Some(this.UserByLogin)
| _ -> None
let foo() : 'b option =
try
longFunction()
with
| exn -> exceptionFunction exn
None
match (useCache, nameToDictionary()) with
| "true", Some(dictionary) ->
match dictionary.Get id with
| Some(result) -> Some(result)
| _ -> match foo() with
| Some(result) -> dictionary.Put id result
Some(result)
| _ -> None
| _ -> foo()
This is not possible - the problem is that the return type of the method would depend on the string that it gets as the input argument. The input string is only known at run-time, but the type needs to be known at compile-time.
You could use the Choice type which lets you return one of multiple different types:
member this.CacheNameToDictionary (cacheName: string) =
match cacheName with
| "userByAutoincrementedId" -> Choice1Of3(this.User)
| "userByLogin" -> Choice2Of3(this.UserByLogin)
| _ -> Choice3Of3()
This works, but the return type lists all three alternatives and is pretty ugly:
Choice<DictionaryCache<int,User>, DictionaryCache<string,User>,unit>
Also, the consumer of this method will have to pattern match on the result and handle the two different dictionaries in different ways, so this might not make your code particularly beautiful.
Honestly, I think that you are adding a level of abstraction that you do not need. If there are two different keys, then you need different code to handle that and it's unlikely that you'll be able to write code that is extensible and adds third kind of dictionary.

Type inference workaround for generic function

Given the following parametric type
type SomeDU2<'a,'b> =
| One of 'a
| Two of 'a * 'b
I have to functions that check if the given param is the respective union case without regard to params
let checkOne x =
match x with
| One _ -> true
| _ -> false
let checkTwo x =
match x with
| Two _ -> true
| _ -> false
This works pretty nice and as expected
let oi = checkOne (One 1)
let os = checkOne (One "1")
let tis = checkTwo (Two (1, "1"))
let tsi = checkTwo (Two ("1", 1))
I can switch the types as I like.
Now However I like to combine those two functions into one creation function
let makeUC () = (checkOne, checkTwo)
and then instantiate like this
let (o,t) = makeUC ()
only it gives me this error message now
Value restriction. The value 'o' has been inferred to have generic type
val o : (SomeDU2<'_a,'_b> -> bool)
Either make the arguments to 'o' explicit or, if you do not intend for it to be generic, add a type annotation.
val o : (SomeDU2<obj,obj> -> bool)
Actually I dont want that - nor do I need that.
Probably its a instance of missing higher kinded types in F#
Is there a way around this?
Edit
Actually me question wasnt complety as per #johns comment below.
Obviously I can do the following
let ro1 = o ((One 1) : SomeDU2<int,int>)
let rt1 = t (Two (1,2))
which then will backwards infer o and t to be of type SomeDU2<int,int> -> bool (I find this backwards inference very strange thou). The problem then is that o wont allow for the below anymore.
let ro2 = o ((One "1") : SomeDU2<string,int>)
So I'd have to instantiate a specific o instance for every combination of generic parameters of SomeDU2.
You would run into the value restriction even without the tuple:
let o = (fun () -> checkOne)()
If you need the results of invoking a function to be applicable to values of any type, then one solution would be to create instances of a nominal type with a generic method:
type DU2Checker =
abstract Check : SomeDU2<'a,'b> -> bool
let checkOne = {
new DU2Checker with
member this.Check(x) =
match x with
| One _ -> true
| _ -> false }
let checkTwo = {
new DU2Checker with
member this.Check(x) =
match x with
| Two _ -> true
| _ -> false }
let makeUC() = checkOne, checkTwo
let o,t = makeUC()
let false = o.Check(Two(3,4))

How to turn this into statically resolved type parameters

I have a need to use statically resolved type parameters in a situation similar to the below:
[<Struct>]
type Wrapper<'T> =
val raw:'T
new(v:'T) = {raw = v}
type Value =
| Float of float
| Int of int
| String of string
with
member this.ToWrapper() :'T =
match this with
| Float f -> Wrapper<float>(f) // type is inferred as float
| Int i -> Wrapper<int>(i) // error
| String s -> Wrapper<string>(s) // error
How do I define and use a ToWrapper function (or set thereof) that can map a 'Value' type to any type within the Generic Wrapper<'T> where I know 'T will be either float | int | string?
The Wrapper<'T> type needs to be a Struct so interfaces aren't an option - as suggested in some other posts related to this.
It's not clear to me what are you trying to achieve. Are you trying to restrict the wrapped types to Int, String and Float?
1) If so you can check at runtime like this:
[<Struct>]
type Wrapper<'T> =
val raw:'T
new(v:'T) = {raw = v}
let wrap x =
match box x with
| :? float -> ()
| :? int -> ()
| :? string -> ()
| _ -> failwith "invalid type"
Wrapper x
let a = wrap 90
let b = wrap "hi"
let c = wrap true // fails at runtime
2) If you want to restrict at compile-time an easy way is to add static members as constructors:
[<Struct>]
type Wrapper<'T> =
val raw:'T
private new(v:'T) = {raw = v}
with
static member wrap (x:float) = Wrapper x
static member wrap (x:int) = Wrapper x
static member wrap (x:string) = Wrapper x
let a = Wrapper<_>.wrap 90
let b = Wrapper<_>.wrap "hi"
let c = Wrapper<_>.wrap true // doesn't compile
3) Or may be, using the DU inside the wrapper makes more sense for you:
type Value =
| Float of float
| Int of int
| String of string
[<Struct>]
type Wrapper =
val raw:Value
new(v:Value) = {raw = v}
Of all solutions 3) is the only one that really restricts your wrapper. Solutions 1) and 2) restrict the way you construct it.
From 2) you can use some tricks with statically resolved type parameters in order to come up with an inline function (not a method) that will wrap only on those types. Still that will not constraint the type itself, but since the constructor is private the code that consumes your type will be forced through your restricted constructors.
Statically resolved type parameters work with functions or methods but not on types, since they are a compile time F# feature, not a .NET type system feature.
You can't do this, because Wrapper<float> isn't the same type as Wrapper<int> (which also isn't the same type as Wrapper<string>). What would the return type of ToWrapper be? It can't be all three at once.

Resources