Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Partial anonymous record in F#

Tags:

f#

How do I type let's say a function that takes a record that has a field a and any other field?

Is some equivalent of this Standard ML function possible in F#?

fun f {a: string, ...} = "Hello" ^ a;

Tried the following but these don't seem to be syntactically valid:

let f (r: {|a: string; _|}) = impl;
let g (r: {|a: string; ...|}) = impl;
like image 918
ᅙᄉᅙ Avatar asked Dec 23 '22 15:12

ᅙᄉᅙ


1 Answers

You can achieve this kind of constraint with Statically Resolved Type Parameters like so:

let inline f< ^T when ^T: (member a: string)> (r: ^T) = ()

f {| a = "yeet" |} // compiles
f {| a = "yeet"; b = "yote" |} // compiles
f {| b = "yote" |} // error

Note that this isn't just for anonymous records. It will hold true for any type that has a member with the specified signature.

I also like to hide these things behind a module and extract the nasty SRTP stuff into an active pattern like so:

module M =
    let inline private (|HasName|) x = (^a : (member Name: string) x)

    let inline printName (HasName name) = printfn $"{name}"

type Person1 = { Name: string; Age: int }
type Person2 = { Name: string; Age: int; IsFunny: bool }

type Name(name) =
    member _.Name = name

let p1 = { Name = "Phillip"; Age = 30 }
let p2 = { Name = "Phillip"; Age = 30; IsFunny = false }
let nm = Name "Phillip"

M.printName p1
M.printName p2
M.printName nm

This has the benefit of hiding the details of how you get the constraint "lined up correctly" and lets you easily re-use the signature in other things you want to publicly expose.

like image 160
Phillip Carter Avatar answered Jan 05 '23 22:01

Phillip Carter