let's say I have this:
module A =
let a () =
printfn "%A" DateTime.UtcNow
is there a way to do something like
type B = A
B.a()
or, any other mechanism that would allow to set up an alias?
You can alias modules in F#, but the syntax is module <NewName> = <OldName>:
module A =
let a () =
printfn "%A" DateTime.UtcNow
module B = A
B.a()
Related
I think my problem is best explained on a hypothetical example.
Suppose you want to design a software for several banks. Two use cases are: calculating a credit score and evaluating taxes. The score does not depend on the taxes and vice versa.
The return type of the credit scoring function is Score and the taxes are returned in TaxAmount. You want the solution as clean and extensible as possible.
Where would you put the shared/not so shared types?
Putting Score and TaxAmount in one shared type module would violate the single responsibility principle.
Putting Score and TaxAmountin different modules increases the number of files/modules/git submodules very quickly as the code base grows. And the problem spreads through all layers higher up which results in a lot of open module statements.
Having each function its own type module results in repetition.
Not using custom domain types results in losing the benefits of custom domain types.
My constructed onion architecture example:
namespace Domain.Shared
module Types =
type Score =
| GOOD
| BAD
type TaxAmount = int
namespace Domain.Scoring1
module Main =
open Domain.Shared.Types
let go input :Score = Score.BAD
namespace Domain.Scoring2
module Main =
open Domain.Shared.Types
let go input :Score = Score.GOOD
namespace Domain.ComputeTaxes1
module Main =
open Domain.Shared.Types
let go input :TaxAmount = 1
namespace Application
module BankA =
let standardize x = "a"
let score input =
input
|> standardize
|> Domain.Scoring1.Main.go
let computeTaxes input =
input
|> standardize
|> Domain.ComputeTaxes1.Main.go
module BankB =
let standardize x = "b"
let score input =
input
|> standardize
|> Domain.Scoring2.Main.go
namespace Infrastructure
module BankA =
let read filename = "A"
let score filename =
filename
|> read
|> Application.BankA.score
let computeTaxes (filename:string) =
filename
|> read
|> Application.BankA.computeTaxes
module BankB =
let read filename = "B"
let score (filename:string) =
filename
|> read
|> Application.BankB.score
module Shell =
[<EntryPoint>]
let main (args: array<string>) =
match args with
| [| "A"; "score"; filename |] -> printfn "%A" (BankA.score filename)
| [| "A"; "tax"; filename |] -> printfn "%A" (BankA.computeTaxes filename)
| [| "B"; "score"; filename |] -> printfn "%A" (BankB.score filename)
| _ -> printfn "Not implemented"
0
For a while F# has supported the ability to auto-quote using [<ReflectedDefinitionAttribute>]. Is there anything similar for laziness?
e.g.
member __.Quoted ([<ReflectedDefinitionAttribute>] quotation:Expr<'T>) = ...
member __.Thunked ([<LazyAttribute>] thunk:Lazy<'T>) = ...
I suppose I could use something like
member __.Quoted ([<ReflectedDefinitionAttribute>] quotation:Expr<'T>) =
Lazy (evaluate (<# fun () -> %quotation #>)) // evaluate using Unquote or similar
But wouldn't this be costly?
UPDATE:
I found a hack, it's not exactly what I would like but it give the correct behavior.
type Signal = Signal with
member __.Return x = x
member __.Delay (f:unit -> _) = f
let a = Signal { return randint }
let b = Signal { return randint }
let c = Signal { return a() + b() }
There is nothing like the ReflectedDefinition attribute for automatically turning things into delayed Lazy<'T> computations.
You are right that automatically quoting the argument achieves something like this. You could use the (very limited) LeafExpressionConverter.EvaluateQuotation to do this for some limited kinds of expressions, but as you note, this would be inefficient. The following is a proof of concept though (but you cannot call custom functions in the branches as this uses LINQ expressions):
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.RuntimeHelpers
type A =
static member If<'T>(c:bool,
[<ReflectedDefinition>] t:Expr<'T>,
[<ReflectedDefinition>] f:Expr<'T>) =
if c then LeafExpressionConverter.EvaluateQuotation t :?> 'T
else LeafExpressionConverter.EvaluateQuotation f :?> 'T
A.If(1 = 2, 0, 1)
In practice, I think a more reasonable approach is to just use the built-in Lazy<'T> values. F# has a (not widely known) lazy keyword that gives you a bit nicer syntax for creating those:
let iff c (t:Lazy<_>) (f:Lazy<_>) =
if c then t.Value else f.Value
iff (1 = 2)
(lazy (printfn "true"; 41))
(lazy (printfn "false"; 42))
I'm writing unit tests in F# using FsUnit and NUnit, with the NUnit test adapter for VS2015 Ultimate CTP. I've come across an odd issue with a module member being null, where I wouldn't expect it to be.
Is this an issue with the the code, or the way the tests are executed?
I've tried changing the signature of Foo.SpecificFooStrategy.create to unit -> FooStrategy (let create = fun () -> ...) and invoking as Foo.SpecificFooStrategy.create (), but that hasn't helped.
Code
namespace Foo
// FooStrategy.fs
module FooStrategy =
type FooStrategy =
| FooStrategy of A * B
with
member x.A =
match x with
| FooStrategy(a, _) -> a
member x.B =
match x with
| FooStrategy(_, b) -> b
let create a b = FooStrategy(a, b)
// SpecificFooStrategy.fs
module SpecificFooStrategy =
let private a = // ...
let private b = // ...
let create =
FooStrategy.create a b
Test
namespace Foo.Tests
[<TestFixture>]
module SpecificFooStrategyTests =
[<Test>]
let ``foo is something`` ()=
let strategy = Foo.SpecificFooStrategy.create
strategy // strategy is null here
|> An.operationWith strategy
|> should equal somethingOrOther
In the code, create is not a function but a value.
It can be fixed by defining it as a function:
let create() = …
and calling it with parens:
let strategy = Foo.SpecificFooStrategy.create()
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.
I need to create a collection in F# that has a key value pair and is global in scope.
You can do this:
[<AutoOpen>]
module Globals =
let map = System.Collections.Generic.Dictionary<_,_>()
Then use it unqualified throughout your program:
map.Add(1, "a")
map.Add(2, "b")
map |> Seq.iter (fun (KeyValue(k, v)) -> printfn "Key: %d, Value: %s" k v)
depending on what kind of project you are doing the best method might be do just declare it in a module:
module GlobalVals =
let myCollection = .... // whatever
you can just use it with
GlobalVals.myCollection...