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.
Related
I'd like to transform my F# OOP version of Tagless Final into a typical FP approach and I'm thinking to use Statically Resolved Type Parameters of Type Classes from OO.
What I've done is
open System
open FSharpPlus
type UserName = string
type DataResult<'t> = DataResult of 't with
static member Map ( x:DataResult<'t> , f) =
match x with
| DataResult t -> DataResult (f t)
creating the SRTP I need
type Cache =
static member inline getOfCache cacheImpl data =
( ^T : (member getFromCache : 't -> DataResult<'t> option) (cacheImpl, data))
static member inline storeOfCache cacheImpl data =
( ^T : (member storeToCache : 't -> unit) (cacheImpl, data))
type DataSource() =
static member inline getOfSource dataSourceImpl data =
( ^T : (member getFromSource : 't -> DataResult<'t>) (dataSourceImpl, data))
static member inline storeOfSource dataSourceImpl data =
( ^T : (member storeToSource : 't -> unit) (dataSourceImpl, data))
and their concrete implementations
type CacheNotInCache() =
member this.getFromCache _ = None
member this.storeCache _ = ()
type CacheInCache() =
member this.getFromCache user = monad {
return! DataResult user |> Some}
member this.storeCache _ = ()
type DataSourceNotInCache() =
member this.getFromSource user = monad {
return! DataResult user }
type DataSourceInCache() =
member this.getFromSource _ =
raise (NotImplementedException())
by which I can define a tagless final DSL
let requestData (cacheImpl: ^Cache) (dataSourceImpl: ^DataSource) (userName:UserName) = monad {
match Cache.getOfCache cacheImpl userName with
| Some dataResult ->
return! map ((+) "cache: ") dataResult
| None ->
return! map ((+) "source: ") (DataSource.getOfSource dataSourceImpl userName) }
and that kind of works as follows
[<EntryPoint>]
let main argv =
let cacheImpl1 = CacheInCache()
let dataSourceImpl1 = DataSourceInCache()
let cacheImpl2 = CacheNotInCache()
let dataSourceImpl2 = DataSourceNotInCache()
requestData cacheImpl1 dataSourceImpl1 "john" |> printfn "%A"
//requestData (cacheImpl2 ) dataSourceImpl2 "john" |> printfn "%A"
0
The problem is that I'm getting the warning
construct causes code to be less generic than indicated by the type
annotations
for both cacheImpl1 and dataSourceImpl1 and so I can't reuse requestData for the other case.
Is there a way to detour this issue?
I'm not familiar with the abstraction you're trying to implement, but looking at your code it seems you're missing an inline modifier here:
let inline requestData (cacheImpl: ^Cache) (dataSourceImpl: ^DataSource) (userName:UserName) = monad {
match Cache.getOfCache cacheImpl userName with
| Some dataResult ->
return! map ((+) "cache: ") dataResult
| None ->
return! map ((+) "source: ") (DataSource.getOfSource dataSourceImpl userName) }
As a side note, you can simplify your map function like this:
type DataResult<'t> = DataResult of 't with
static member Map (DataResult t, f) = DataResult (f t)
I am familiar with final tagless, but I'm not sure why you would use SRTPs.
Final tagless uses type classes, and these can be emulated with interfaces (see the way scala emulates typeclasses).
The approach is similar to (basically the same) as "object algebra", which can be implemented using standard OO constructs.
EDIT: possible solutions at bottom
I'm doing some data work where I need to be very careful about string lengths that will eventually be sent in fixed width text output, stored in limited size nvarchar fields, etc. I want to have good strict typing for these rather than naked System.String types.
Suppose I've got some code like this to represent these, with a few useful module functions that play nicely with Result.map, Option.map, etc.
module String40 =
let private MaxLength = 40
type T = private T of string
let create (s:string) = checkStringLength MaxLength s |> Result.map T
let trustCreate (s:string) = checkStringLength MaxLength s |> Result.okVal |> T
let truncateCreate (s:string) = truncateStringToLength MaxLength s |> T
let toString (T s) = s
type T with
member this.AsString = this |> toString
module String100 =
let private MaxLength = 100
type T = private T of string
let create (s:string) = checkStringLength MaxLength s |> Result.map T
let trustCreate (s:string) = checkStringLength MaxLength s |> Result.okVal |> T
let truncateCreate (s:string) = truncateStringToLength MaxLength s |> T
let toString (T s) = s
type T with
member this.AsString = this |> toString
Obviously these are almost entirely repetitive with only the module name and max length different in each block.
What options are available to try and cut down on the repetitiveness here? I would love to have something like this:
type String40 = LengthLimitedString<40>
type String100 = LengthLimitedString<100>
tryToRetrieveString () // returns Result<string, ERRType>
|> Result.bind String40.create
T4 code generation doesn't seem to be an option for F# projects
Type providers seem like overkill for this kind of simple templating, and as best as I can tell they can only produce classes, not modules.
I'm aware of Scott Wlaschin's constrained strings page, but I end up with roughly the same level of repetitive code in the 'Create a type', 'Implement IWrappedString', 'create a public constructor' steps he lists.
These code blocks are fairly short and it wouldn't be the end of the world to just copy/paste a dozen times for the different field lengths. But I feel like I'm missing a simpler way to do this.
UPDATE:
One other note is that it's important that records using these types give information about what type they're carrying:
type MyRecord =
{
FirstName: String40;
LastName: String100;
}
and not be something like
type MyRecord =
{
FirstName: LimitedString;
LastName: LimitedString;
}
Tomas' answer, using the Depended Type Provider nuget library is a pretty good one, and would be a good solution for a lot of people who are fine with its behavior as-is. I felt like it would be a little tricky to extend and customize unless I wanted to maintain my own copy of a type provider which I was hoping to avoid.
Marcelo's suggestion of static parameter constraints was a fairly productive path of research. They give me basically what I was looking for -- a generic argument that is basically an 'interface' for a static methods. However the kicker is that they require inline functions to operate and I don't have the time to evaluate how much that would or would not matter in my code base.
But I took that and altered it to use regular generic constraints. It's a bit goofy to have to instantiate an object to get a max-length value, and fsharp type/generic code is just gross to look at, but from the module users's perspective it's clean, and I can easily extend this however I want.
type IMaxLengthProvider = abstract member GetMaxLength: unit -> int
type MaxLength3 () = interface IMaxLengthProvider with member this.GetMaxLength () = 3
type MaxLength4 () = interface IMaxLengthProvider with member this.GetMaxLength () = 4
module LimitedString =
type T< 'a when 'a :> IMaxLengthProvider> = private T of string
let create< 't when 't :> IMaxLengthProvider and 't : (new:unit -> 't)> (s:string) =
let len = (new 't()).GetMaxLength()
match checkStringLength len s with
| Ok s ->
let x : T< 't> = s |> T
x |> Ok
| Error e -> Error e
let trustCreate< 't when 't :> IMaxLengthProvider and 't : (new:unit -> 't)> (s:string) =
let len = (new 't()).GetMaxLength()
match checkStringLength len s with
| Ok s ->
let x : T< 't> = s |> T
x
| Error e ->
let msg = e |> formErrorMessage
failwith msg
let truncateCreate< 't when 't :> IMaxLengthProvider and 't : (new:unit -> 't)> (s:string) =
let len = (new 't()).GetMaxLength()
let s = truncateStringToLength len s
let x : T< 't> = s |> T
x
let toString (T s) = s
type T< 'a when 'a :> IMaxLengthProvider> with
member this.AsString = this |> toString
module test =
let dotest () =
let getString () = "asd" |> Ok
let mystr =
getString ()
|> Result.bind LimitedString.create<MaxLength3>
|> Result.okVal
|> LimitedString.toString
sprintf "it is %s" mystr
I think the BoundedString type provider from the Dependent type provider project lets you do exactly what you need. Using the example from the project documentation, you can do e.g.:
type ProductDescription = BoundedString<10, 2000>
type ProductName = BoundedString<5, 50>
type Product = { Name : ProductName; Description : ProductDescription }
let newProduct (name : string) (description : string) : Product option =
match ProductName.TryCreate(name), ProductDescription.TryCreate(description) with
| Some n, Some d -> { Name = n; Description = d }
| _ -> None
I don't know how many people use this project in practice, but it seems quite simple and it does exactly what you were asking for, so it might be worth a try.
Using a touch of careful reflection magic, we can achieve a lot and get some really nice types. How about something like this?
module Strings =
type [<AbstractClass>] Length(value: int) =
member this.Value = value
let getLengthInst<'L when 'L :> Length> : 'L =
downcast typeof<'L>.GetConstructor([||]).Invoke([||])
type LimitedString<'Length when 'Length :> Length> =
private | LimitedString of maxLength: 'Length * value: string
member this.Value =
let (LimitedString(_, value)) = this in value
member this.MaxLength =
let (LimitedString(maxLength, _)) = this in maxLength.Value
module LimitedString =
let checkStringLength<'L when 'L :> Length> (str: string) =
let maxLength = getLengthInst<'L>.Value
if str.Length <= maxLength then Ok str
else Error (sprintf "String of length %d exceeded max length of %d" str.Length maxLength)
let create<'L when 'L :> Length> (str: string) =
checkStringLength<'L> str
|> Result.map (fun str -> LimitedString (getLengthInst<'L>, str))
open Strings
// === Usage ===
type Len5() = inherit Length(5)
type Len1() = inherit Length(1)
// Ok
LimitedString.create<Len5> "Hello"
// Error
LimitedString.create<Len1> "world"
One option might be to use a single module for limited-length strings that uses curried parameters for the length-limit and the string itself, then just partially apply the limit parameter. An implementation might look like this:
module LimitedString =
type T = private T of string
let create length (s:string) = checkStringLength length s |> Result.map T
let trustCreate length (s:string) = checkStringLength length s |> Result.okVal |> T
let truncateCreate length (s:string) = truncateStringToLength length s |> T
let toString (T s) = s
type T with
member this.AsString = this |> toString
Then, your modules for each length would still be required, but wouldn't have all the boilerplate:
module String100 =
let create = LimitedString.create 100
let trustCreate = LimitedString.trustCreate 100
let truncateCreate = LimitedString.truncateCreate 100
EDIT
After reading the comment and the update to the original post, I would change my suggestion a bit. Instead of defining the T type inside each module, I would have a specific struct-type single-case union for each string length at the top level. Then, I would move to toString to the individual string modules. Finally, I would add one more parameter to the LimitedString module to allow us to partially apply both the length and the specific single-case union type:
[<Struct>] type String40 = private String40 of string
[<Struct>] type String100 = private String100 of string
module LimitedString =
let create length ctor (s:string) = checkStringLength length s |> Result.map ctor
let trustCreate length ctor (s:string) = checkStringLength length s |> Result.okVal |> ctor
let truncateCreate length ctor (s:string) = truncateStringToLength length s |> ctor
module String40 =
let create = LimitedString.create 40 String40
let trustCreate = LimitedString.trustCreate 40 String40
let truncateCreate = LimitedString.truncateCreate 40 String40
let toString (String40 s) = s
module String100 =
let create = LimitedString.create 100 String100
let trustCreate = LimitedString.trustCreate 100 String100
let truncateCreate = LimitedString.truncateCreate 100 String100
let toString (String100 s) = s
type MyRecord =
{
FirstName: String40
LastName: String100
}
There's still a fair amount of boilerplate here, but I think this is in the ballpark for a solution using single-case unions and modules. A Type Provider might be possible, but you would have to consider whether the added complexity outweighs the boilerplate.
Can't you use Static Parameters, as shown on the F#.Data package's HtmlProvider example?
I have a need to use statically resolved type parameters in a situation similar to the below:
[<Struct>]
type Wrapper<'T> =
val raw:'T
new(v:'T) = {raw = v}
type Value =
| Float of float
| Int of int
| String of string
with
member this.ToWrapper() :'T =
match this with
| Float f -> Wrapper<float>(f) // type is inferred as float
| Int i -> Wrapper<int>(i) // error
| String s -> Wrapper<string>(s) // error
How do I define and use a ToWrapper function (or set thereof) that can map a 'Value' type to any type within the Generic Wrapper<'T> where I know 'T will be either float | int | string?
The Wrapper<'T> type needs to be a Struct so interfaces aren't an option - as suggested in some other posts related to this.
It's not clear to me what are you trying to achieve. Are you trying to restrict the wrapped types to Int, String and Float?
1) If so you can check at runtime like this:
[<Struct>]
type Wrapper<'T> =
val raw:'T
new(v:'T) = {raw = v}
let wrap x =
match box x with
| :? float -> ()
| :? int -> ()
| :? string -> ()
| _ -> failwith "invalid type"
Wrapper x
let a = wrap 90
let b = wrap "hi"
let c = wrap true // fails at runtime
2) If you want to restrict at compile-time an easy way is to add static members as constructors:
[<Struct>]
type Wrapper<'T> =
val raw:'T
private new(v:'T) = {raw = v}
with
static member wrap (x:float) = Wrapper x
static member wrap (x:int) = Wrapper x
static member wrap (x:string) = Wrapper x
let a = Wrapper<_>.wrap 90
let b = Wrapper<_>.wrap "hi"
let c = Wrapper<_>.wrap true // doesn't compile
3) Or may be, using the DU inside the wrapper makes more sense for you:
type Value =
| Float of float
| Int of int
| String of string
[<Struct>]
type Wrapper =
val raw:Value
new(v:Value) = {raw = v}
Of all solutions 3) is the only one that really restricts your wrapper. Solutions 1) and 2) restrict the way you construct it.
From 2) you can use some tricks with statically resolved type parameters in order to come up with an inline function (not a method) that will wrap only on those types. Still that will not constraint the type itself, but since the constructor is private the code that consumes your type will be forced through your restricted constructors.
Statically resolved type parameters work with functions or methods but not on types, since they are a compile time F# feature, not a .NET type system feature.
You can't do this, because Wrapper<float> isn't the same type as Wrapper<int> (which also isn't the same type as Wrapper<string>). What would the return type of ToWrapper be? It can't be all three at once.
In F#, we can create interface instance by object expression, but while I'm trying to use attribute ReflectedDefinition on the instance method, then I cannot get the quotations. The method info is declared in the interface type, not the instance type.
Here is my test code:
module Test
open System
open System.Reflection
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Quotations.DerivedPatterns
open Microsoft.FSharp.Quotations.ExprShape
type IMyInterface =
abstract Foo : int -> int
let createMyInterface () =
{ new IMyInterface with
[<ReflectedDefinition>]
member this.Foo a = a + 1 }
let expr =
let a = createMyInterface()
<# a.Foo(42) #>
let rec iterExpr (expr:Expr) =
match expr with
| Call(objectExpr, info, paramExprs) ->
printfn "info: %A" info
printfn "reflected type: %A" info.ReflectedType
match info with
| MethodWithReflectedDefinition methodExpr ->
printfn "%A" methodExpr
| _ -> failwith "No reflected definition"
| ShapeVar _ -> failwithf "TODO: %A" expr
| ShapeLambda _ -> failwithf "TODO: %A" expr
| ShapeCombination _ -> failwithf "TODO: %A" expr
let test() =
iterExpr expr
[<EntryPoint>]
let main argv =
test()
0 // return an integer exit code
If I run it, I got exception:
C:\Users\Xiang\Documents\Inbox\TTTT\bin\Debug>TTTT
info: Int32 Foo(Int32)
reflected type: Test+IMyInterface
Unhandled Exception: System.Exception: No reflected definition
at Microsoft.FSharp.Core.Operators.FailWith[T](String message)
at Test.iterExpr(FSharpExpr expr) in C:\Users\Xiang\Documents\Inbox\TTTT\Program.fs:line 30
at Test.test() in C:\Users\Xiang\Documents\Inbox\TTTT\Program.fs:line 37
at Test.main(String[] argv) in C:\Users\Xiang\Documents\Inbox\TTTT\Program.fs:line 41
And I also checked the generated assembly with dotPeek, it is implemented as a derived class:
[CompilationMapping(SourceConstructFlags.ObjectType)]
[Serializable]
public interface IMyInterface
{
int Foo([In] int obj0);
}
[CompilationMapping(SourceConstructFlags.Closure)]
[Serializable]
[SpecialName]
[StructLayout(LayoutKind.Auto, CharSet = CharSet.Auto)]
internal sealed class createMyInterface\u004014 : Test.IMyInterface
{
public createMyInterface\u004014()
{
base.\u002Ector();
Test.createMyInterface\u004014 createMyInterface14 = this;
}
[ReflectedDefinition]
int Test.IMyInterface.Test\u002DIMyInterface\u002DFoo([In] int obj0)
{
return obj0 + 1;
}
}
So, the problem is, when I call the Foo method in quotation, the Call pattern get MethodInfo which is declared at interface type, which has no definition. So how could I get the actually implementation MethodInfo? and then I can get the quotation of the implementation?
Here's your problem in a nutshell:
You're calling a virtual method through an instance of the type where the method is defined.
You want the quotation to contain a call to the method as defined on the derived class.
This won't work, and isn't limited to interfaces or object expressions:
type A() =
abstract M : unit -> unit
default this.M() = printfn "abstract"
type T() =
inherit A() with
[<ReflectedDefinition>]
override this.M() = printfn "override"
let expr =
let a : A = upcast T()
<# a.M() #>
Fundamentally, the whole point of an object expression is to provide an anonymous implementation of a non-sealed class, so what you're asking for doesn't make sense to me - the compiler only knows that the object is some instance implementing that interface but can't know the concrete type of the instance and therefore can't know which (of potentially many) concrete method to use.
I have the following ViewModelBase in F# which I'm trying to build to learn F# with WPF.
module MVVM
open System
open System.Collections.ObjectModel
open System.ComponentModel
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
open System.Reactive.Linq
module Property =
let ToName(query : Expr) =
match query with
| PropertyGet(a, b, list) ->
b.Name
| _ -> ""
let SetValue<'t>(obj, query : Expr<'t>, value : 't) =
match query with
| PropertyGet(a, b, list) ->
b.SetValue(obj, value)
| _ -> ()
let GetValue<'o, 't>(obj : 'o , query : Expr<'t>) : option<'t> =
match query with
| PropertyGet(a, b, list) ->
option.Some(b.GetValue(obj) :?> 't )
| _ -> option.None
let Observe<'t>(x: INotifyPropertyChanged) (p : Expr<'t>) =
let name = ToName(p)
x.PropertyChanged.
Where(fun (v:PropertyChangedEventArgs) -> v.PropertyName = name).
Select(fun v -> GetValue(x, p).Value)
type ViewModelBase() =
let propertyChanged = new Event<_, _>()
interface INotifyPropertyChanged with
[<CLIEvent>]
member x.PropertyChanged = propertyChanged.Publish
abstract member OnPropertyChanged: string -> unit
default x.OnPropertyChanged(propertyName : string) =
propertyChanged.Trigger(x, new PropertyChangedEventArgs(propertyName))
member x.SetValue<'t>(expr : Expr<'t>, v : 't) =
Property.SetValue(x, expr, v)
x.OnPropertyChanged(expr)
member x.OnPropertyChanged<'t>(expr : Expr<'t>) =
let propName = Property.ToName(expr)
x.OnPropertyChanged(propName)
However I get an error from the compiler
Error 1 The type 'ViewModelBase' is used in an invalid way.
A value prior to 'ViewModelBase' has an inferred type involving
'ViewModelBase', which is an invalid forward reference.
However the compiler doesn't tell me what value prior is the offending part of the problem. As I'm pretty new to the type inference as used by F# I'm probably missing an obvious problem.
FYI the code is meant to be used like the below but at the moment this code is commented out and the error is only pertaining to the core code above
type TestModel() as this =
inherit MVVM.ViewModelBase()
let mutable name = "hello"
let subscription = (Property.Observe this <# this.SelectedItem #>).
Subscribe(fun v -> Console.WriteLine "Yo")
member x.SelectedItem
with get() = name
and set(v) =
x.SetValue(<# x.SelectedItem #>, v)
I found it.
let SetValue<'t>(obj, query : Expr<'t>, value : 't) =
match query with
| PropertyGet(a, b, list) ->
b.SetValue(obj, value)
| _ -> ()
was under constrained. Should be
let SetValue<'t>(obj : Object, query : Expr<'t>, value : 't) =
match query with
| PropertyGet(a, b, list) ->
b.SetValue(obj, value)
| _ -> ()