Suppose I have 2 record types
type A = { a: string; parameters: parameter list }
type B = { b: string; parameters: parameter list }
where
type parameter = { name: string; value : string }
How can I write function parameter
let parameter name value entity =
{ entity with parameters = List.append
parameters
[ { name = name; value = value; } ]
}
Such as
let a = { a = "a", parameters = [] } |> parameter "p", "v" // a is a record of type A
let b = { b = "b", parameters = [] } |> parameter "p", "v" // b is record of type B
It is not idiomatic F#, but this can be done using SRTP. I assume you have simplified the use-case for StackOverflow, but if A and B are really not related types, then I think you should revisit your overall program design.
I defined a Parameter type as this:
type Parameter =
{
Name : string
Value : string
}
Now, we need to add a method to types A and B that implement the addition of a parameter:
type A =
{
A : string
Parameters : Parameter list
}
with
member this.AddParameter(p : Parameter) =
{
this with
Parameters =
p :: this.Parameters
}
And...
type B =
{
B : string
Parameters : Parameter list
}
with
member this.AddParameter(p : Parameter) =
{
this with
Parameters =
p :: this.Parameters
}
Then we can write an inline function that calls this method:
let inline addParameter (p : Parameter) (x : ^t) : ^t =
(^t : (member AddParameter : Parameter -> ^t) (x, p))
Here ^t will be replaced with A or B (or whatever) depending on the call-site. The syntax for SRTP isn't great, but it is better in F# 7.
Usage:
let p = { Name = "p"; Value = "abc" }
let a : A =
{ A = "a"; Parameters = [] }
|> addParameter p
printfn $"%A{a}"
let b : B =
{ B = "b"; Parameters = [] }
|> addParameter p
printfn $"%A{b}"
Related
I have this code and the intent is to only print cases of the Union Type Event:
type CustomerPromoted = { id: string; level: int }
type CustomerCreated = { id: string; companyName: string }
type Event =
| Created of CustomerCreated
| Promoted of CustomerPromoted
let printEvents (events: Event list) =
events
|> List.iter (fun event -> printfn "%A" event)
[<EntryPoint>]
let main argv =
let created = { id = "1"; companyName = "MS" }
let promoted = { id = "1"; level = 3 }
printEvents [ created, promoted ]
0
But the line
printEvents [ created, promoted ]
Results in this error:
This expression was expected to have type 'Event' but here has type ''a * 'b'
How do I solve this?
I could just use let printEvents (events) but then I would have this signature: 'a list -> unit and I want to have Event list -> unit
[ created, promoted ] is a one element list of tuples of type CustomerCreated * CustomerPromoted. So the first fix would be to use the list separator ; instead of the tuple separator ,. [ created; promoted ]
Second fix is to note that discriminated unions aren't aliases. Created is of type Event that contains data of the type CustomerCreated. So changing let created = { id = "1"; companyName = "MS" } to let created = Created { id = "1"; companyName = "MS" } would create the type you want.
I wonder if there is a better way of implementing a function that accepts records and modify them.
So I have entities of two types, both have corresponding files on the disk:
type Picture = { Artist : string; File : string }
type Book = { Author : string; File : string }
I want generic function that can copy both pictures and books. In the OOP world I would probably create common interface IArtefact { File : string }, implement it in both records and then create Move method that works on it. Something like:
let move<'a:IArtefact>(a : 'a) (path : string) =
File.Move(a.File, path)
{ a with File = path }
However I suppose that F# does not support such concept. What is the F# way of doing so?
This is possible, why wouldn't it be ;)
type IArtefact =
abstract File: string
type Picture =
{ Artist : string; File : string }
interface IArtefact with
member this.File = this.File
let p = { Artist = "test"; File = "test2" }
(p :> IArtefact).File
Edit: If you want to handle updates:
type IArtefact =
abstract File: string
abstract WithFile: string -> IArtefact
type Picture =
{ Artist : string; File : string }
interface IArtefact with
member this.File = this.File
member this.WithFile(file) = { this with File = file } :> IArtefact
While there is no generic way of change-copying records, there is one for moving anything that has a File:
let inline move from where : string =
let oldFile = (^T : (member File : string) from)
do() // move here
where
type Picture = { Artist: string; File: string }
type Book = { Author: string; File: string }
let p = { Artist = "Vincent van Gogh"; File = "Rooftops" }
let p' = { p with File = move p "The Potato Eaters" }
let b = { Author = "Don Syme"; File = "Generics in CLR" }
let b' = { b with File = move b "Expert F#" }
This could then be expanded to move anything that knows how to be moved:
let inline move' a where =
let oldFile = (^T : (member File : string) a)
do() // move here
(^T: (member moved : string -> ^T) a, where)
type Picture' =
{ Artist: string; File: string } with
member this.moved where = { this with File = where }
type Book' =
{ Author: string; File: string } with
member this.moved where = { this with File = where }
let p2 = { Artist = "Vincent van Gogh"; File = "Rooftops" }
let p2' = move' p2 "The Potato Eaters"
let b2 = { Author = "Don Syme"; File = "Generics in CLR" }
let b2' = move' b2 "Expert F#"
Does the compiler create a new location in memory when a record is extended (deep copy?) or does the compiler make the record mutable and modify the value?
For example:
type MyRecord = { A : string
; B : string
}
let record = { A = "A"; B = "B" }
let record = { record with A = "new A" } //copy or overwrite?
Since I am overwriting record does the compiler copy or overwrite? Are there performance concerns either way?
It makes the copy.
Copy-and-update Record expression
*A copy-and-update record expression elaborates as if it were a record expression written as follows:
let v = expr in { field-label1 = expr1 ; … ; field-labeln = exprn; F1 = v.F1; ... ; FM = v.FM }
where F1 ... FM are the fields of R that are not defined in field-initializers and v is a fresh variable.*
This
type T = {
A : string
B : string
}
let x = { A = "a"; B = "b" }
let y = { x with A = "aa" }
is equivalent to this
class T {
public readonly string A;
public readonly string B;
public T(string a, string b) {
A = a;
B = b;
}
}
var x = new T("a", "b");
var y = new T("aa", x.B);
Given i have the type:
type NewsMessage(identifier:string, headline:string)
and this record:
type NewsMessageParams = {
identifier:string
headline:string
}
Is there an implicit way to adapt the record into the class constructor?
Something like so:
let newsMessageParmas = {identifier=""; headline=""}
new NewsMessage(newsMessageParams) //this is where i need help
You can do this with tuples using the ||> syntax. I would like to do this with a record.
Something like this would work:
let toClass {identifier = i; headline = h} = NewsMessage(i, h)
// Usage
let newsMessageParams = {identifier = ""; headline = ""}
let newsMessage = newsMessageParams |> toClass
Note that object constructor isn't first-class in F#, so you have more chances of using pipe operators if declaring NewsMessage as discriminated unions:
type NewsMessage = NewsMessage of string * string
let toTuple {identifier = i; headline = h} = i, h
let newsMessageParams = {identifier = ""; headline = ""}
let newsMessage = newsMessageParams |> toTuple |> NewsMessage
in F#, I want to make a type of indexed array, so I can access the element by either .[i] or .[index_names] and by slice notation with index .. Is it possible to overwrite .[] like this? thanks.
define overloaded indexer in your type:
type MyIndexedArray<'T>() =
member this.Item(i : int) : 'T = Unchecked.defaultof<_>
member this.Item(name : string) : 'T = Unchecked.defaultof<_>
member this.GetSlice(a : int option, b : int option) : 'T = Unchecked.defaultof<_>
let arr = new MyIndexedArray<int>()
let a = arr.[1]
let b = arr.["name"]
let c = arr.[1..2]
let d = arr.[1..]
let e = arr.[..3]
let f = arr.[*]