F# Invoke Function in Module - f#

I'm trying to write an installer for my application that runs (mostly) a bunch of database migrations.
I have a very simple start here, I'm just trying to figure out what invokeMethod should equal.
let RunMigration (fsModule: System.Type) =
fsModule.GetProperties()
|> Array.filter (fun property -> property.Name = "run")
|> Array.map(fun property -> property |> invokeMethod ; "Ran successfully")
|> String.concat "<br/>"
let RunAllMigrations =
Assembly
.GetExecutingAssembly()
.GetTypes()
|> Array.filter FSharpType.IsModule
|> Array.filter (fun fsModule -> fsModule.Namespace = "DredgePos.Migrations")
|> Array.map RunMigration
|> String.concat "<br/><hr/>"
At the moment as you can see it is a fairly simple process of
Get all the types in the assembly
Getting all modules in the Migrations namespace.
Then if that module has a function named run, I want to invoke it.
How do I invoke it?
EDIT:
The run method will always be Unit -> Unit
let run () = ()

In your RunMigration method, you are getting a list of properties of the type - but a function declared as in your example should be compiled as a method. For example, if I have the following module in the current assembly:
module XX =
let run () = printfn "hello!"
It should be possible to invoke this if the RunMigration function first finds the run method and then calls the Invoke operation with no arguments (and null as the instance, since this is a static method):
let RunMigration (fsModule: System.Type) =
let mi = fsModule.GetMethod("run")
mi.Invoke(null,[||]) |> ignore
This returns obj value, which will be null (as the method returns unit) and so you can typically ignore the result.

Related

Issue with generic taking some unrelated type, in F#

I have the following code:
let exchangeInfo, marginBrackets, accountInfo =
async {
let failOrExtract = function
| Ok d -> d
| Error (e: ExchangeError) -> e.Describe |> tee logger.Fatal |> failwith
let pf = processResult >> failOrExtract
let! e = rest.FuturesUsdt.System.GetExchangeInfoAsync() |> Async.AwaitTask
let! b = rest.FuturesUsdt.GetBracketsAsync() |> Async.AwaitTask
let! i = rest.FuturesUsdt.Account.GetAccountInfoAsync() |> Async.AwaitTask
return pf e, pf b, pf i
} |> Async.RunSynchronously
In short, it calls 3 async rest functions, then sends them through failOrExtract which is made of 2 parts: the processResult part that returns a Result<'a, ExchangeError> type, with 'a different for each of these calls, and then failOrExtract that either fails or returns the data field.
this will not compile, the compiler says:
[FS0001] The type 'BinanceFuturesUsdtExchangeInfo' does not match the type 'System.Collections.Generic.IEnumerable<BinanceFuturesSymbolBracket>'```
It looks like the failOrExtract function was generic on the first use, but not afterwards since pf e compiles, but pf b and pf i have the same error.
why am I getting this error?
I think you're running into the F# "value restriction", although the compiler isn't being clear about it. The problem here is that pf is defined as a value, not a function, so the compiler is inferring that it has a concrete, non-generic type. In order to give pf a generic signature, try changing it to this instead:
let pf x = x |> processResult |> failOrExtract

F# - How to test an exception raised in the constructor with fsunit?

I want to check if an argument passed to the constructor of a type is valid.
I check it and raise an ArgumentException if not valid.
I want to create a test for this behavior. I want to use Assert.throws or preferably FSUnit instead of a try/with block.
#package "FsUnit#3.4.1"
#package "nunit#3.11.0"
open System
open FSUnit
type configuration = {aaa:int}
type Client(conf:configuration) =
do
if conf.aaa < 3 then raise (ArgumentException("aaa must be at least 3"))
member this.do_something() =
()
// TEST
// 1. does not "compile"
Assert.Throws<ArgumentException>(fun () -> Client(configuration) |> ignore)
// 2. does not work
//Assert.Throws<ArgumentException>( fun () ->
// let a = Client(configuration);
// a
// |> ignore)
// 3. does not work
(fun() -> Client(configuration)) |> ignore |> should throw typeof<ArgumentException>
// 4. OK but... bleah!
try
Client(configuration) |> ignore
Assert.Fail()
with
| :? ArgumentException -> Assert.Pass() |> ignore
| _ -> Assert.Fail()
Your first approach works fine for me - I just had to define configuration which is not included in your question but, presumably, is defined somewhere in your actual file. The following compiles and behaves as expected for me:
let configuration = { aaa = 1 }
Assert.Throws<ArgumentException>(fun () -> Client(configuration) |> ignore)
Your second code snippet does not work because it has ignore in the wrong place - you are ignoring the entire function (which contains the code that you want to test) and then you are passing unit to the assertion. The ignore call needs to be inside of the function so that it ignores the result of calling the constructor. The following works for me:
(fun() -> Client(configuration) |> ignore) |> should throw typeof<ArgumentException>

Is there already or can I declare a more pipe friendly upcast?

I want to be able to just
let upcast'<'T,'TResult when 'T :> 'TResult> (y:'T) = y |> upcast
However, that then constrains 'T to be 'TResult instead of it being something that can be cast to 'TResult
I know I can
|> fun x -> x :> 'TResult
|> fun x -> upcast x
|> fun x -> x :> _
but then if I'm doing anything else on that line I have to go back and put () around the fun x -> upcast x or it thinks what I'm doing is part of the fun x function.
can I define or does there exist a way to be able to
|> upcast |> doesn't work
|> ( ( :> ) 'TResult) doesn't work and is messy
edit
In response to Thomas Petricek - minimal failing auto-upcast sample:
module Test =
let inline f'<'t>():IReadOnlyCollection<'t> =
List.empty
|> ResizeArray
|> System.Collections.ObjectModel.ReadOnlyCollection
|> fun x -> x :> IReadOnlyCollection<_>
let inline f<'t> () :IReadOnlyCollection<'t> =
List.empty
|> ResizeArray
|> System.Collections.ObjectModel.ReadOnlyCollection
As far as I know, specifying the kind of constraint between 'T and 'TResult is not possible. There is a related question about this with links to more information and a feature request.
That said, I wonder why do you need this? The F# compiler is able to insert upcasts automatically, even when using pipes, so if you want to do this as part of a longer pipe, it should not be needed. Here is a simple illustration:
type Animal = interface end
type Dog = inherit Animal
let makeDog () = { new Dog }
let consumeAnimal (a:Animal) = 0
makeDog () |> consumeAnimal
I guess you might need pipe-able upcast if you wanted to have it at the end of the pipeline, but then I'd just do the upcast on a separate line. Or is your question motivated by some more complicated cases where the implicit upcast does not work?
EDIT 1: Here is a minimal example using ReadOnlyCollection and IReadOnlyList which works:
let foo () : System.Collections.ObjectModel.ReadOnlyCollection<int> = failwith "!"
let bar (x:System.Collections.Generic.IReadOnlyList<int>) = 0
foo() |> bar
EDIT 2: To comment on the update - the problem here is that automatic upcasts are only inserted when passing arguments to functions, but in the second example, the type mismatch is between the result of the pipe and the return type of the function. You can get that to work by adding an identity function of type IReadOnlyCollection<'T> -> IReadOnlyCollection<'T> to the end of the pipe:
let inline f<'t> () :IReadOnlyCollection<'t> =
List.empty
|> ResizeArray
|> System.Collections.ObjectModel.ReadOnlyCollection
|> id<IReadOnlyCollection<_>>
This works, because now the upcast is inserted automatically when passing the argument to the id function - and this then returns a type that matches with the return type of the function.
much simpler and unexpected
let inline f2<'t>() : IReadOnlyCollection<'t> =
List.empty
|> ResizeArray
|> System.Collections.ObjectModel.ReadOnlyCollection
:> _

F# how to list functions with custom attribute?

I'm trying to create some kind of interface, but i cannot find how to use custom attributes in F# as MSDN only shows usage of CLR attributes. This is what i want to achieve:
open System
type Command (name : string) =
inherit Attribute()
member this.Name = name
[<Command("something")>]
let doSomething () =
Console.Write("I'm doing something")
[<Command("somethingElse")>]
let doSomethingElse () =
Console.Write("I'm doing something else")
[<EntryPoint>]
let main args =
let command = Console.ReadLine()
// find function where Command.Name = command and call it
Console.Read()
0
To extend on your answer, a more generic approach would be to get all the types and then filter the functions that have the attribute you're looking for (as your approach would break down once your application grows and no longer has everything "packed" into the Program class):
let getCommands () =
let types = Assembly.GetExecutingAssembly().GetTypes()
let commands =
types
|> Array.collect (fun typ -> typ.GetMethods())
|> Array.choose (fun mi ->
mi.CustomAttributes
|> Seq.tryFind (fun attr -> attr.AttributeType = typeof<Command>)
|> Option.map (fun attr -> attr, mi))
let commandsMap =
commands
|> Seq.map (fun (attr, mi) ->
let name =
let arg = attr.ConstructorArguments.[0]
unbox<string> arg.Value
name, mi)
|> Map.ofSeq
commandsMap
This gets all the functions from all the types in the executing assembly, then filters out everything that doesn't have command attribute. Then it builds a map where the key is the attribute argument and the value is the MethodInfo of the function.
Ok, found it.
Reflection.Assembly.GetExecutingAssembly().GetType("Program").GetMethods()
Program typename is not viable in code so it cannot be used in typeof<Program>, but this type exists and can be taken from assembly.

Can I call a function by name in f#?

Is there any way to call a function by name in F#? Given a string, I want to pluck a function value from the global namespace (or, in general, a given module), and call it. I know the type of the function already.
Why would I want to do this? I'm trying to work around fsi not having an --eval option. I have a script file that defines many int->() functions, and I want to execute one of them. Like so:
fsianycpu --use:script_with_many_funcs.fsx --eval "analyzeDataSet 1"
My thought was to write a trampoline script, like:
fsianycpu --use:script_with_many_funcs.fsx trampoline.fsx analyzeDataSet 1
In order to write "trampoline.fsx", I'd need to look up the function by name.
There is no built-in function for this, but you can implement it using .NET reflection. The idea is to search through all types available in the current assembly (this is where the current code is compiled) and dynamically invoke the method with the matching name. If you had this in a module, you'd have to check the type name too.
// Some sample functions that we might want to call
let hello() =
printfn "Hello world"
let bye() =
printfn "Bye"
// Loader script that calls function by name
open System
open System.Reflection
let callFunction name =
let asm = Assembly.GetExecutingAssembly()
for t in asm.GetTypes() do
for m in t.GetMethods() do
if m.IsStatic && m.Name = name then
m.Invoke(null, [||]) |> ignore
// Use the first command line argument (after -- in the fsi call below)
callFunction fsi.CommandLineArgs.[1]
This runs hello world when called by:
fsi --use:C:\temp\test.fsx --exec -- "hello"
You can use reflection to get the functions as MethodInfo's by FSharp function name
open System
open System.Reflection
let rec fsharpName (mi:MemberInfo) =
if mi.DeclaringType.IsNestedPublic then
sprintf "%s.%s" (fsharpName mi.DeclaringType) mi.Name
else
mi.Name
let functionsByName =
Assembly.GetExecutingAssembly().GetTypes()
|> Seq.filter (fun t -> t.IsPublic || t.IsNestedPublic)
|> Seq.collect (fun t -> t.GetMethods(BindingFlags.Static ||| BindingFlags.Public))
|> Seq.filter (fun m -> not m.IsSpecialName)
|> Seq.groupBy (fun m -> fsharpName m)
|> Map.ofSeq
|> Map.map (fun k v -> Seq.exactlyOne v)
You can then invoke the MethodInfo
functionsByName.[fsharpFunctionNameString].Invoke(null, objectArrayOfArguments)
But you probably need to do more work to parse your string arguments using the MethodInfo.GetParameters() types as a hint.
You could also use FSharp.Compiler.Service to make your own fsi.exe with an eval flag
open System
open Microsoft.FSharp.Compiler.Interactive.Shell
open System.Text.RegularExpressions
[<EntryPoint>]
let main(argv) =
let argAll = Array.append [| "C:\\fsi.exe" |] argv
let argFix = argAll |> Array.map (fun a -> if a.StartsWith("--eval:") then "--noninteractive" else a)
let optFind = argv |> Seq.tryFind (fun a -> a.StartsWith "--eval:")
let evalData = if optFind.IsSome then
optFind.Value.Replace("--eval:",String.Empty)
else
String.Empty
let fsiConfig = FsiEvaluationSession.GetDefaultConfiguration()
let fsiSession = FsiEvaluationSession(fsiConfig, argFix, Console.In, Console.Out, Console.Error)
if String.IsNullOrWhiteSpace(evalData) then
fsiSession.Run()
else
fsiSession.EvalInteraction(evalData)
0
If the above was compiled into fsieval.exe it could be used as so
fsieval.exe --load:script_with_many_funcs.fsx --eval:analyzeDataSet` 1

Resources