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]
Related
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.
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
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.
From the MSDN documentation I understand that if Run is implemented it will be called automatically at the end of the computational expression. It says that:
builder.Run(builder.Delay(fun () -> {| cexpr |}))
will be generated for the computational expression. Run and/or Delay will be omitted if they are not defined in the workflow builder. I was expecting my ReaderBuilder to return a list of MyItem objects when Run is called automatically. So I do not understand why I'm getting a type mismatch error. The errors are generated by the return statement inside the ProcedureBuilder foo at the end of my code listing here. Could someone please explain what I'm misunderstanding about workflow builders and what I have implemented incorrectly?
I'm getting the following errors:
The type ''a list' is not compatible with the type 'ReaderBuilder'
Type constraint mismatch. The type 'a list is not compatible with type ReaderBuilder The type ''a list' is not compatible with the type 'ReaderBuilder'
open System
open System.Data
open System.Data.Common
open System.Configuration
let config = ConfigurationManager.ConnectionStrings.Item("db")
let factory = DbProviderFactories.GetFactory(config.ProviderName)
type Direction =
| In
| Out
| Ref
| Return
type dbType =
| Int32
| String of int
type ReaderBuilder(cmd) =
let mutable items = []
member x.Foo = 2
member x.YieldFrom item =
items <- item::items
item
member x.Run item =
items
type ProcBuilder(procedureName:string) =
let name = procedureName
let mutable parameters = []
let mutable cmd:DbCommand = null
let mutable data = []
member x.Command with get() = cmd
member x.CreateCommand() =
factory.CreateCommand()
member x.AddParameter(p:string*dbType*Direction) =
parameters <- p::parameters
member x.Bind(v,f) =
f v
member x.Reader = ReaderBuilder(cmd)
member x.Return(rBuilder:ReaderBuilder) =
data
let (?<-) (builder:ProcBuilder) (prop:string) (value:'t) =
builder.Command.Parameters.[prop].Value <- value
type MyItem() =
let mutable _a = 0
let mutable _b = String.Empty
let mutable _c = DateTime.Now
member x.a
with get() = _a
and set n = _a <- n
member x.b
with get() = _b
and set n = _b <- n
member x.c
with get() = _c
and set n = _c <- n
let proc name = ProcBuilder(name)
let (%) (builder:ProcBuilder) (p:string*dbType*Direction) =
builder.AddParameter(p)
builder
let (?) (r:DbDataReader) (s:string) = r.GetOrdinal(s)
let foo x y =
let foo = proc "foo" % ("x", Int32, In) % ("y", String(15), In)
foo?x <- x
foo?y <- y
foo {
do! foo?x <- x
do! foo?y <- y
return foo.Reader {
let item = MyItem()
item.a <- r.GetInt32("a")
item.b <- r.GetString("b")
item.c <- r.GetDateTime("c")
yield! item
}
}
The problem in your example is that the foo.Reader { ... } block has a return type MyItem list (because this is what the Run member of the ReaderBuilder type returns). However, the Return member of ProcBuilder expects an argument of type ReaderBuilder.
The data field of ReaderBuilder will be always an empty list, so this is also suspicious. I think you probably want to change the Return of ProcBuilder to take an argument MyItem list instead.
However, I think that using custom computation builder for database access doesn't really give you much advantage. You're not creating a "non-standard computation" in some sense. Instead, you probably just want a nice syntax for calling commands & reading data. Using the dynamic operator can make this quite elegant even without computation builders - I wrote an article about this some time ago.
Been working with a lot of TimeSpans recently, and have a need to get sums & averages.
However, TimeSpan defines neither operator get_Zero nor DivideByInt, so Seq.sum and Seq.average can't be used directly with this type. The following fails to compile:
open System
type System.TimeSpan
with
static member Zero with get() = TimeSpan()
static member (/) (n:DateTime, d:int) = DateTime( n.Ticks / (int64) d )
let ts = [ TimeSpan(10L); TimeSpan(99L) ]
let sum = ts |> Seq.sum
let avg = ts |> Seq.average
Error: The type 'TimeSpan' does not support any operators named 'get_Zero'
Error: The type 'TimeSpan' does not support any operators named 'DivideByInt'
Warning: Extension members cannot provide operator overloads. Consider defining the operator as part of the type definition instead.
Is there some F# magic that can define these operators on an existing type?
I know the following will work (and should be more efficient to boot), but I'm still curious about the above so I can add it to my toolbox for use with other types.
let sum = TimeSpan( ts |> Seq.sumBy (fun t -> t.Ticks) )
let avg = TimeSpan( let len = ts |> Seq.length in sum.Ticks / int64 len )
As far as I know, static member constraints (that are used by functions like Seq.sum) are not able to discover members that are added by type extensions (essentially, extension methods), so I don't think there is a direct way of doing this.
The best option I can think of is to creat a simple wrapper around the System.TimeSpan struct. Then you can define all the required members. The code would look like this:
[<Struct>]
type TimeSpan(ts:System.TimeSpan) =
member x.TimeSpan = ts
new(ticks:int64) = TimeSpan(System.TimeSpan(ticks))
static member Zero = TimeSpan(System.TimeSpan.Zero)
static member (+) (a:TimeSpan, b:TimeSpan) =
TimeSpan(a.TimeSpan + b.TimeSpan)
static member DivideByInt (n:TimeSpan, d:int) =
TimeSpan(n.TimeSpan.Ticks / (int64 d))
let ts = [ TimeSpan(10L); TimeSpan(99L) ]
let sum = ts |> Seq.sum
let avg = ts |> Seq.average
I called the type TimeSpan, so it hides the standard System.TimeSpan type. However, you still need to write ts.TimeSpan when you need to access the underlying system type, so this isn't as nice as it could be.
Mhh the following is rather ugly, but it works. Does it help? I define a wrapper for TimeSpan that can implicitly be converted back to a TimeSpan.
type MyTimeSpan(ts : TimeSpan) =
member t.op_Implicit : TimeSpan = ts
static member (+) (t1 : MyTimeSpan, t2 : MyTimeSpan) =
new MyTimeSpan(TimeSpan.FromTicks(t1.op_Implicit.Ticks + t2.op_Implicit.Ticks))
static member Zero = new MyTimeSpan(TimeSpan.Zero)
static member DivideByInt (t : MyTimeSpan, i : int) =
new MyTimeSpan(TimeSpan.FromTicks(int64 (float t.op_Implicit.Ticks / float i)))
let toMyTS ts = new MyTimeSpan(ts)
let l = [TimeSpan.FromSeconds(3.); TimeSpan.FromSeconds(4.)]
|> List.map toMyTS
|> List.average