I have the following code:
[<AutoOpen>]
module OptionOperators =
let private mapOption func arg =
match arg with
| None -> Ok None
| Some arg -> func arg |> Result.map Some
let ( ^> ) arg func =
mapOption func arg
[<AutoOpen>]
module Model =
// Constrained to be no more than 35 chars, not null or empty, used for tag
type String35 = private String35 of string
module String35 =
let create field str =
ConstrainedType.createString field String35 35 str
type UnvalidatedAddress = { Name: string option }
type Address = { Name: String35 option }
let validate dto =
result {
let! name = dto.Name ^> String35.create "Name"
}
Is ^> the reasonable infix operator for the combine of Result and Option?
And is there a list of infix operators?
You're losing readability by using that obscure operator. I suggest you look for something more meaningful that anyone reading your code can understand:
let private validateField validator arg =
match arg with
| None -> Ok None
| Some arg -> validator arg |> Result.map Some
which you can use as:
let validate dto =
result {
let! name = dto.Name
|>validateField (String35.create "Name")
}
Related
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.
With reference to Is there an equivalent of C#'s nameof(..) in F#?
how can the nameof function used or extended for the following case?
let nameof (q:Expr<_>) =
match q with
| Patterns.Let(_, _, DerivedPatterns.Lambdas(_, Patterns.Call(_, mi, _))) -> mi.Name
| Patterns.PropertyGet(_, mi, _) -> mi.Name
| DerivedPatterns.Lambdas(_, Patterns.Call(_, mi, _)) -> mi.Name
| _ -> failwith "Unexpected format"
let any<'R> : 'R = failwith "!"
let s = _nameof <# System.Char.IsControl #> //OK
type A<'a>() =
static member MethodWith2Pars(guid:Guid, str:string) = ""
static member MethodWith2Pars(guid:Guid, ba:byte[]) = ""
let s1 = nameof <# A<_>.MethodWith2Pars #> //Error FS0503 A member or object constructor 'MethodWith2Pars' taking 1 arguments is not accessible from this code location. All accessible versions of method 'MethodWith2Pars' take 2 arguments
let s2 = nameof <# A<_>.MethodWith2Pars : Guid * string -> string #> //Same error
The compiler gives the following error:
Error FS0503 A member or object constructor 'MethodWith2Pars' taking 1 arguments is not accessible from this code location. All accessible versions of method 'MethodWith2Pars' take 2 arguments
The answer you linked is a bit outdated. F# 5.0 (released just recently) offers the true nameof feature. See the announcement: https://devblogs.microsoft.com/dotnet/announcing-f-4-7/#nameof
This feature also existed in preview since F# 4.7: https://devblogs.microsoft.com/dotnet/announcing-f-4-7/#nameof
You can write your code like this:
open System
type A() =
static member MethodWith2Pars(guid:Guid, str:string) = ""
static member MethodWith2Pars(guid:Guid, ba:byte[]) = ""
let s1 = nameof (A.MethodWith2Pars : Guid * byte[] -> string)
let s2 = nameof (A.MethodWith2Pars : Guid * string -> string)
The type annotation is needed due to overloading. Not sure why there is a generic type parameter on the class declaration, but it's not used anywhere so I just removed it.
I would like to get the value of a field in a Record by looking it up with a string.
type Test = { example : string }
let test = { example = "this is the value" }
let getByName (s:string) =
???? //something like test.GetByName(s)
Standard .net reflection should be working fine for such scenario. Record fields are exposed as properties, so you can just query the type with reflection API.
It could look like this:
let getByName (s:string) =
match typeof<Test>.GetProperties() |> Array.tryFind (fun t -> t.Name = s)
with
| Some pi -> Some(pi.GetValue(test))
| None -> None
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.
I'm a newbie F# programmer, and I'm having some trouble getting the syntax for my F# program correct.
Essentially, I want to turn this C# code into F#:
class MyRiskyObject : BaseObject
{
private string field;
public MyRiskyObject(object foo, string data)
: base(foo)
{
try
{
this.data = RiskyOperation(data);
}
catch (ArgumentException)
{
DoSomethingElse();
}
}
}
I, so far, have something like
type MyRiskyObject
inherit BaseObject
val field : string
new (foo:object, data:string) = {
inherit BaseObject(foo)
try
field = RiskyOperation()
????????
}
I just can't get the syntax right...
Edit:
here is the actual code I'm working on:
type RegExpObject =
inherit CommonObject
val RegExp : Regex
val Global : bool
member x.IgnoreCase:bool =
(x.RegExp.Options &&& RegexOptions.IgnoreCase) = RegexOptions.IgnoreCase
member x.MultiLine:bool =
(x.RegExp.Options &&& RegexOptions.Multiline) = RegexOptions.Multiline
new (env, pattern, options, global') =
{
inherit CommonObject(env, env.Maps.RegExp, env.Prototypes.RegExp)
// Here, I need to catch the exception, and instead call RaiseSyntaxError.
RegExp = new Regex(pattern, options ||| RegexOptions.ECMAScript ||| RegexOptions.Compiled)
Global = global'
}
then RegExp = new Regex(pattern, options ||| RegexOptions.ECMAScript ||| RegexOptions.Compiled)
new (env, pattern) =
RegExpObject(env, pattern, RegexOptions.None, false)
Why not just delegate the real work to a separate free function, so the work doesn't have to be done directly in your class' constructor?
let createRegex pattern options =
try
Regex(pattern, options ||| RegexOptions.ECMAScript ||| RegexOptions.Compiled)
with
| :? System.ArgumentException -> RaiseSynaxError ()
Then your class (as demonstrated in your edit) would be:
type RegExpObject(env, pattern, options, global') //'
inherit CommonObject(env, env.Maps.RegExp, env.Prototypes.RegExp)
let RegExp = createRegex pattern options
let Global = global' //'
member x.IgnoreCase =
(x.RegExp.Options &&& RegexOptions.IgnoreCase) = RegexOptions.IgnoreCase
member x.MultiLine =
(x.RegExp.Options &&& RegexOptions.Multiline) = RegexOptions.Multiline
new (env, pattern) = RegExpObject(env, pattern, RegexOptions.None, false)
An advantage here is that, as #Brian indicated, no use of val would be necessary, making the class definition much cleaner.
Try the following
type MyRiskyObject(foo : obj, data : string) as this =
inherit BaseObject(foo)
let mutable data = data;
do
try
data <- this.RiskyOperation data
with
| :? System.ArgumentException -> this.DoSomethingElse()
Alternate example with non-mutable let binding where RiskOperation and DoSomethingElse are not members of MyRiskObject
type MyRiskyObject(foo : obj, data : string) =
inherit BaseObject(foo)
let data =
try
OtherModule.RiskyOperation data
with
| :? System.ArgumentException ->
OtherModule.DoSomethingElse()
data