Record transformation - f#

Suppose I have the following type:
type Temp<'b,'c> =
{
A : string
B : 'b
C : 'c
D : string
}
And I want to create a function that receives Temp<string,string> and outputs Temp<int,int>. I tried two approaches. The most cumbersome (f1) works and the most logical (in my view) does not (f2).
let f1 (r : Temp<string,string>) = //works
{
A = r.A
B = r.B |> int
C = r.C |> int
D = r.D
}
//doesn't work
let f2 (r : Temp<string,string>) = {r with B = r.B |> int; C = r.C |> int}
Is there another way to construct such a function without having to repeat all the fields in the body?

As mentioned before, you can not (ATM) use the f2 approach but you can simply create a generic version of your f1 approach and use it.
type Temp<'b,'c> = {
A: string
B: 'b
C: 'c
D: string
}
module Temp =
let bind fB fC temp =
{
A = temp.A
B = fB temp.B
C = fC temp.C
D = temp.D
}
let bind1 f = bind f f
let sTemp: Temp<string, string> = {
A = "a"
B = "b"
C = "c"
D = "d"
}
let iTemp: Temp<int, int> = sTemp |> Temp.bind int int // with two separate functions for each generic field
let iTemp: Temp<int, int> = sTemp |> Temp.bind1 int // with one function for both fields at once

I don't think so. A copy and update record expression can only be used to create a new record of the same type, but Temp<string, string> and Temp<int, int> are not the same type. I think there are a number of suggestions to broaden this along the lines you suggest in the F# language issue tracker (e.g. this one), but AFAIK, none have made it into the language yet.

Related

Partial anonymous record in 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;
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.

Carrying out same function on different F# record types with identical labels

Suppose I have following record types and their lists:
type Employee = {
id:int
name:string
}
type Project = {
id:int
name:string
}
let el = [{Employee.id = 1; name = "E1"};{Employee.id = 2; name = "E2"};{Employee.id = 3; name = "E3"};]
let pl = [{Project.id = 5; name = "P1"};{Project.id = 6; name = "P2"};{Project.id = 7; name = "P3"};]
I want to apply the same function(as defined below) to both type lists but the type inferred is Project.
let CreateFormattedStringList l =
l |> List.map(fun x -> (x.id |> string) + "#" + x.name)
//function signature:
//val CreateFormattedStringList : l:Project list -> string list
let res_1 = el |> CreateFormattedStringList //error
let res_2 = pl |> CreateFormattedStringList //ok
I found this helpful link which shows a simple value returned. So, the following works for both types of lists in my case:
let inline CreateFormattedStringList (l: ^T list) =
(^T: (member id:int) (l.Head))
Now I am unable to wrap my head around how to apply the more elaborate function in same way. Something like:
let inline CreateFormattedStringList (l: ^T list) =
l |> List.map(fun (^T: (member id:int) (x)) -> (x.id |> string) + "#" + x.name)
//error
I am trying to find examples but aren't able to. How can I use inline to be able to apply the same function to both types? Also, how to add constraint for 'name' and 'id' both?
Firstly, I think it's simpler to write a function that works on a single item instead of a list and then use it with other higher order functions like List.map if necessary.
The syntax for this is confusing, but what you had working so far was actually a function that contains an expression that uses the id member, while also asserting that the input type has an id member. So you need to add another expression for name. It's easier to tell what's going on if you bind those to names:
let inline formatIdName (x: ^T) =
let id = (^T: (member id:int) x)
let name = (^T: (member name:string) x)
sprintf "%i - %s" id name
formatIdName {Employee.id = 1; name = "E1"} // "1 - E1"
formatIdName {Project.id = 5; name = "P1"} // "5 - P1"

Make WebSharper generate simple field access

I need a type that will be translated into a plain JS object so that F# field access will be translated into simple JS field access (this is necessary, since the object will be sent through postMessage, so it'll lose all its methods).
Again, I need
let x = a.b
to be translated into
var x = a.b;
without any method calls.
Here is a slightly modified example from the F# Reference:
namespace T
open IntelliFactory.WebSharper
[<JavaScript>]
module A =
type MyClass =
val a : int
val b : int
new(a0, b0) = { a = a0; b = b0; }
let myClassObj = new MyClass(35, 22)
let x = myClassObj.b
This won't translate with
x: error : Failed to translate property access: b.'
Ok, let's make those vals mutable:
namespace T
open IntelliFactory.WebSharper
[<JavaScript>]
module A =
type MyClass =
val mutable a : int
val mutable b : int
new(a0, b0) = { a = a0; b = b0; }
let myClassObj = new MyClass(35, 22)
let x = myClassObj.b
This will be successfully translated, but… MyClass.New returns an empty object. The question now starts looking much like a bugreport, right? So, back to the question.
Are there any other ways to achieve what I want?
There are additional issues with record-style constructors "{x = y}". I will have to look into this again on F# 3.0, the older F# did not produce sensible quotations for those and we did some partial workarounds in WebSharper. Right now your example breaks. So here is the working code with a static method instead of a constructor:
type MyClass private () =
[<DefaultValue>]
val mutable a : int
[<DefaultValue>]
val mutable b : int
static member Create(a0, b0) =
let c = MyClass()
c.a <- a0
c.b <- b0
c
let test () =
let myClassObj = MyClass.Create(35, 22)
let x = myClassObj.a
let y = myClassObj.b
JavaScript.Log(x, y)
Trivially, a record would also work.
In some cases where you want to go really low-level you can annotate members with the Inline attribute. When this is too much overhead you can use untyped API:
let x = obj ()
x?a <- 1
let y = x?a
JavaScript.Log(x, y)
try this:
type MyClass (a, b) =
member val A = a with get
member val B = b with get
let myClassObj = new MyClass(35, 22)
let x = myClassObj.B

F# generate a sequence/array of dates

In F# I can easily do
let a = [1 .. 10];;
Then why can't I do
let a = DateTime.Parse("01/01/2012")
let b = DateTime.Parse("01/01/2020")
let dateList = [a .. b]
It gives an error Type constraint mismatch. The type DateTime is not compatible with type TimeSpan
There are two problems - firstly you need to specify the interval you want to use between elements of the list. This would be a TimeSpan, however it does not have a static Zero member.
This constraint is required by the skip range operator which requires the 'step' type to have static (+) and Zero members
You can define your own structure which supports the required operations however:
type TimeSpanW = { span : TimeSpan } with
static member (+) (d:DateTime, wrapper) = d + wrapper.span
static member Zero = { span = new TimeSpan(0L) }
You can then do:
let ts = new TimeSpan(...)
let dateList = [a .. {span = ts} .. b]
Edit: Here's an alternative syntax using discriminated unions that you may prefer:
type Span = Span of TimeSpan with
static member (+) (d:DateTime, Span wrapper) = d + wrapper
static member Zero = Span(new TimeSpan(0L))
let ts = TimeSpan.FromDays(1.0)
let dateList = [a .. Span(ts) .. b]
Here's a funky way of generating a list of dates. Note I'm taking no credit for this whatsoever as I got it from someone else.
open System
let a = new DateTime(2013,12,1)
let b = new DateTime(2013,12,5)
Seq.unfold (fun d -> if d < b then Some(d, d.AddDays(1.0)) else None) a
|> Seq.toList;;
It returns:
val it : DateTime list = [01/12/2013 00:00:00; 02/12/2013 00:00:00;
03/12/2013 00:00:00; 04/12/2013 00:00:00]

Can F# Quotations be used to create a function applicable to arbitrary F# record types?

Given an F# record:
type R = { X : string ; Y : string }
and two objects:
let a = { X = null ; Y = "##" }
let b = { X = "##" ; Y = null }
and a predicate on strings:
let (!?) : string -> bool = String.IsNullOrWhiteSpace
and a function:
let (-?>) : string -> string -> string = fun x y -> if !? x then y else x
is there a way to use F# quotations to define:
let (><) : R -> R -> R
with behaviour:
let c = a >< b // = { X = a.X -?> b.X ; Y = a.Y -?> b.Y }
in a way that somehow lets (><) work for any arbitrary F# record type, not just for R.
Short: Can quotations be used to generate F# code for a definition of (><) on the fly given an arbitrary record type and a complement function (-?>) applicable to its fields?
If quotations cannot be used, what can?
You could use F# quotations to construct a function for every specific record and then compile it using the quotation compiler available in F# PowerPack. However, as mentioned in the comments, it is definitely easier to use F# reflection:
open Microsoft.FSharp.Reflection
let applyOnFields (recd1:'T) (recd2:'T) f =
let flds1 = FSharpValue.GetRecordFields(recd1)
let flds2 = FSharpValue.GetRecordFields(recd2)
let flds = Array.zip flds1 flds2 |> Array.map f
FSharpValue.MakeRecord(typeof<'T>, flds)
This function takes records, gets their fields dynamically and then applies f to the fields. You can use it to imiplement your operator like this (I'm using a function with a readable name instead):
type R = { X : string ; Y : string }
let a = { X = null ; Y = "##" }
let b = { X = "##" ; Y = null }
let selectNotNull (x:obj, y) =
if String.IsNullOrWhiteSpace (unbox x) then y else x
let c = applyOnFields a b selectNotNull
The solution using Reflection is quite easy to write, but it might be less efficient. It requires running .NET Reflection each time the function applyOnFields is called. You could use quotations to build an AST that represents the function that you could write by hand if you knew the record type. Something like:
let applyOnFields (a:R) (b:R) f = { X = f (a.X, b.X); Y = f (a.Y, b.Y) }
Generating the function using quotations is more difficult, so I won't post a complete sample, but the following example shows at least a part of it:
open Microsoft.FSharp.Quotations
// Get information about fields
let flds = FSharpType.GetRecordFields(typeof<R>) |> List.ofSeq
// Generate two variables to represent the arguments
let aVar = Var.Global("a", typeof<R>)
let bVar = Var.Global("b", typeof<R>)
// For all fields, we want to generate 'f (a.Field, b.Field)` expression
let args = flds |> List.map (fun fld ->
// Create tuple to be used as an argument of 'f'
let arg = Expr.NewTuple [ Expr.PropertyGet(Expr.Var(aVar), fld)
Expr.PropertyGet(Expr.Var(bVar), fld) ]
// Call the function 'f' (which needs to be passed as an input somehow)
Expr.App(???, args)
// Create an expression that builds new record
let body = Expr.NewRecord(typeof<R>, args)
Once you build the right quotation, you can compile it using F# PowerPack. See for example this snippet.

Resources