Here is a very simple class with static property:
[<AbstractClass; Sealed>]
type test () =
static member ttc = (new Random()).Next()
When I access ttc, it always changes... like this: (in fsi.exe)
What is my purpose is to store values in static member:
type typDimTablesArray () =
static member DimApplication = typDimTables.DimApplication |> Seq.toArray
static member DimApplicationState = typDimTables.DimApplicationState |> Seq.toArray
static member DimDatetime = typDimTables.DimDatetime |> Seq.toArray
static member DimDbQuery = typDimTables.DimDbQuery |> Seq.toArray
static member DimDeveloper = typDimTables.DimDeveloper |> Seq.toArray
static member DimPlatform = typDimTables.DimPlatform |> Seq.toArray
But each time I access typDimTablesArray.DimDatetime.Length
It just query the database again and never store the data in the static member...
Here's a short example of the differences:
type Test3() =
let random = new System.Random()
let y = random.Next()
member __.X = random.Next()
member __.Y = y
static member val Z = (new Random()).Next()
let x = Test3()
x.X
x.X
x.Y
x.Y
Test3.Z
Test3.Z
Also, you could just create an instance of your type and pass in whatever object you need to work on. Now if it's something lazy you might need to cache it:
let rnd = new System.Random()
let rnds = Seq.init 10 (fun _ -> rnd.Next())
type Test4(rndsX:int seq) =
let xx = rndsX |> Seq.cache
member __.Length = xx |> Seq.toArray |> Seq.length
member __.First = xx |> Seq.head
member __.Last = xx |> Seq.last
Related
demo is here https://fsprojects.github.io/SQLProvider/core/mysql.html
type sql = SqlDataProvider<
dbVendor,
connString,
ResolutionPath = resPath,
IndividualsAmount = indivAmount,
UseOptionTypes = useOptTypes,
Owner = "HR"
>
let ctx = sql.GetDataContext()
let employees =
ctx.Hr.Employees
|> Seq.map (fun e -> e.ColumnValues |> Seq.toList)
|> Seq.toList
I defined some custom pipeline operators for Async and Task objects, but get compile error FS0002.
[<AutoOpen>]
module AsyncOperators =
type AsyncOperatorHelper = AsyncOperatorHelper with
static member (=>) (computation, AsyncOperatorHelper) =
fun cont ->
async.Bind(computation, cont)
static member (=>) (computation: Task<'a>, AsyncOperatorHelper) =
fun cont ->
async.Bind(computation |> Async.AwaitTask, cont)
static member (=>) (computation: Task, AsyncOperatorHelper) =
fun cont ->
async.Bind(computation |> Async.AwaitTask, cont)
static member (=->) (cont: 'a -> Task<'b>, AsyncOperatorHelper) =
cont >> Async.AwaitTask
static member (=->) (cont: 'a -> Task, AsyncOperatorHelper) =
cont >> Async.AwaitTask
static member (=->) (cont: 'a -> Async<'t>, AsyncOperatorHelper) =
cont
let inline (|~>) a b = (a => AsyncOperatorHelper) (b =-> AsyncOperatorHelper)
let inline (>~>) a b x = x |> (a =-> AsyncOperatorHelper) |~> (b =-> AsyncOperatorHelper)
let _test () =
let x = async.Return 0
let add1 = fun (s: int) -> (async.Return(s + 1))
// COMPILE OK !!
let y = x |~> add1
// But if I expand add1 in the expression:
// error FS0002: This function takes too many arguments, or is used in a context where a function is not expected
let z = x |~> (fun (s: int) -> (async.Return(s + 1)))
()
The two expressions look the same, but why the second one get FS0002 error? How can I fix the problem?
I'm trying to write a computational workflow which would allow a computation which can produce side effects like log or sleep and a return value
A usage example would be something like this
let add x y =
compute {
do! log (sprintf "add: x = %d, y= %d" x y)
do! sleep 1000
let r = x + y
do! log (sprintf "add: result= %d" r)
return r
}
...
let result = run (add 100 1000)
and I would like the side effects to be produced when executeComputation is called.
My attempt is
type Effect =
| Log of string
| Sleep of int
type Computation<'t> = Computation of Lazy<'t * Effect list>
let private bind (u : 'u, effs : Effect list)
(f : 'u -> 'v * Effect list)
: ('v * Effect list) =
let v, newEffs = f u
let allEffects = List.append effs newEffs
v, allEffects
type ComputeBuilder() =
member this.Zero() = lazy ((), [])
member this.Return(x) = x, []
member this.ReturnFrom(Computation f) = f.Force()
member this.Bind(x, f) = bind x f
member this.Delay(funcToDelay) = funcToDelay
member this.Run(funcToRun) = Computation (lazy funcToRun())
let compute = new ComputeBuilder()
let log msg = (), [Log msg]
let sleep ms = (), [Sleep ms]
let run (Computation x) = x.Force()
...but the compiler complains about the let! lines in the following code:
let x =
compute {
let! a = add 10 20
let! b = add 11 2000
return a + b
}
Error FS0001: This expression was expected to have type
'a * Effect list
but here has type
Computation<'b> (FS0001)
Any suggestions?
The main thing that is not right with your definition is that some of the members of the computation builder use your Computation<'T> type and some of the other members use directly a pair of value and list of effects.
To make it type check, you need to be consistent. The following version uses Computation<'T> everywhere - have a look at the type signature of Bind, for example:
let private bind (Computation c) f : Computation<_> =
Computation(Lazy.Create(fun () ->
let u, effs = c.Value
let (Computation(c2)) = f u
let v, newEffs = c2.Value
let allEffects = List.append effs newEffs
v, allEffects))
type ComputeBuilder() =
member this.Zero() = Computation(lazy ((), []))
member this.Return(x) = Computation(lazy (x, []))
member this.ReturnFrom(c) = c
member this.Bind(x, f) = bind x f
member this.Delay(funcToDelay:_ -> Computation<_>) =
Computation(Lazy.Create(fun () ->
let (Computation(r)) = funcToDelay()
r.Value))
[<ReflectedDefinition>]
module Foo =
let x = 5
let y () = 6
let z a = a
I tried to find out how to get the AST in this situation a couple of times now and keep failing. Time to ask the question here.
So far, I thought that a module would be mappped to a class with static members internally and as such, it should be the equivalent of:
[<ReflectedDefinition>]
type Foo =
static member x = 5
static member y () = 6
static member z a = a
let bar_members =
typeof<Bar>.GetMethods()
|> Array.filter (fun mi -> match mi with | MethodWithReflectedDefinition x -> true | _ -> false)
|> Array.map (fun m -> sprintf "%s: %A" (m.Name) (Expr.TryGetReflectedDefinition(m :> MethodBase) ) )
In the latter case, I could use typeof<Foo>.GetMembers() (or GetMethods()?!), cast it to Reflection.MethodBase and use this as an argument for Expr.TryGetReflectedDefinition().
But unfortunately, this is not working with the module version.
So, how to do it?
If you want to play with the code, you might want to open some namespaces:
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.DerivedPatterns
open Microsoft.FSharp.Reflection
open System.Reflection
The problem comes go down to actually getting the type of the Module. In order to do that, there's a great answer here by Phillip Trelford: https://stackoverflow.com/a/14706890/5438433
Basically, you add a helper value to your module which returns the type of that module:
[<ReflectedDefinition>]
module Foo =
type internal IMarker = interface end
let fooType = typeof<IMarker>.DeclaringType
let x = 5
let y () = 6
let z a = a
You can then use fooType to retrieve the reflected definitions.
let foo_members =
Foo.fooType.GetMethods()
|> Array.filter (fun mi -> match mi with | MethodWithReflectedDefinition x -> true | _ -> false)
|> Array.map (fun m -> sprintf "%s: %A" (m.Name) (Expr.TryGetReflectedDefinition(m :> MethodBase) ) )
I can then, e.g. print the results:
[|"get_fooType: Some PropertyGet (Some (Call (None, TypeOf, [])), DeclaringType, [])";
"get_x: Some Value (5)";
"y: Some Lambda (unitVar0, Value (6))";
"z: Some Lambda (a, a)"|]
For the use case, when the reflected definitions are in another assembly (like an F# dll, for example), you can do without the marker interface trick, as shown below:
open System
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.DerivedPatterns
open Microsoft.FSharp.Reflection
open System.Reflection
open FSharp.Reflection.FSharpReflectionExtensions
let tryGetReflectedModules (a : Assembly) : seq<TypeInfo> =
a.DefinedTypes
|> Seq.filter
(fun dt ->
dt.CustomAttributes
|> Seq.map (fun cad -> cad.AttributeType)
|> Seq.filter ((=) (typeof<ReflectedDefinitionAttribute>))
|> Seq.isEmpty
|> not
)
let astFromReflectedDefinition (mi : MethodInfo) : Expr option =
mi :> MethodBase |> Expr.TryGetReflectedDefinition
let reflectedMethodsOfAModule (m : System.Type) : (MethodInfo * Expr) [] =
m.GetMethods()
|> Array.map (fun m -> (m,astFromReflectedDefinition m))
|> Array.filter (snd >> Option.isSome)
|> Array.map (fun (x,y) -> (x, Option.get y))
let reflectAssembly (assemblyPath : string) =
let a = System.Reflection.Assembly.LoadFile(assemblyPath)
a
|> tryGetReflectedModules
|> Seq.map (fun x -> (x,reflectedMethodsOfAModule (x.AsType())))
Where, for example, the assembly I used for testing the code above looked like this:
namespace Input
[<ReflectedDefinition>]
module Api =
let trace s =
for _ in [0..3] do System.Diagnostics.Trace.WriteLine s
[<ReflectedDefinition>]
module Foo =
let foobar (x : string) : string =
x.ToUpper()
You get the top level types in the assembly, which just so happen to be the (static) classes, representing the modules of the Fsharp assembly and test for the ReflectedDefinitionAttribute presence. Then, you take it from there.
The following code sample is from Scott Wlaschin's site F# for fun and profit.
type LoggingBuilder() =
let log p = printfn "expression is %A" p
member this.Bind(x, f) =
log x
f x
member this.Return(x) =
x
let logger = new LoggingBuilder()
let loggedWorkflow =
logger
{
let! x = 42
let! y = 43
let! z = x + y
return z
}
Is there a way to inject a function instead of printfn into the LoggingBuilder()?
You can just add a parameter to the builder type:
type LoggingBuilder(lf: obj -> unit) =
let log p = lf p
member this.Bind(x, f) =
log x
f x
member this.Return(x) =
x
let logger = new LoggingBuilder(printfn "expression is %A")
You could make the builder generic if you want to make the input type more specific than obj e.g.
type LoggingBuilder<'a>(lf: 'a -> unit) =
...
let logger = new LoggingBuilder<int>(printfn "Got %i")
If your intention is to replace printfn with a logger such as NLog, you can use Printf.ksprintf.
open NLog
open NLog.Config
open NLog.Targets
let private logger =
let config = new LoggingConfiguration()
let consoleTarget = new ColoredConsoleTarget()
config.AddTarget("console", consoleTarget)
consoleTarget.Layout <- Layouts.SimpleLayout.FromString
#"${longdate}|${level:uppercase=true}|${logger}|${message}"
let rule = new LoggingRule("*", LogLevel.Debug, consoleTarget)
config.LoggingRules.Add rule
LogManager.Configuration <- config
LogManager.GetLogger "MyLogger"
type LoggingBuilder(lf: string -> unit) =
let log format =
let doAfter (s: string) = lf s
Printf.ksprintf doAfter format
member this.Bind(x, f) =
log "%A" x
f x
member this.Return(x) =
x
let logger = new LoggingBuilder(logger.Info)