I want to use the following code to restrict there is only one argument. However, I got the following error at first :: NIL?
Error 1 This expression was expected to have type
string []
but here has type
'a list
[<EntryPoint>]
let main argv =
match argv with
| first :: NIL ->
.... do something with first
| _ -> failwith "Must have only one argument."
The command line arguments are passed as an array, not a list.
Do something like this if you expect exactly one argument:
match argv with
| [|first|] ->
// .... do something with first
| _ -> failwith "Must have only one argument."
As mentioned in the accepted answer the "args" argument to the entrypoint is an array, not a list, so you cannot use it with the syntax for list matching.
Instead of matching on the array, as suggested above, you could turn the arguments into an actual list and use that for matching. I have found that a very useful way to handlie command line arguments (though it may be overkill for your example case). As an example:
[<EntryPoint>]
let main args =
let arglist = args |> List.ofSeq
match arglist with
| first :: [] ->
// do something with 'first'
| _ -> // catches both the no-argument and multi-argument cases
printfn "Usage : "
// print usage message
Edit:
As for more complicated examples there are two ways to go from here. You can of course add more complicated cases in the match, or you could parse the list of arguments in a recursive way to build an object representing options and arguments. The latter would get a bit too complicated to fit here, but as an example of some more complex match cases, here is some code related to some recent work where the executable accepts a "command" to operate on a target file, and each command has different further arguments (each command calls a function whose implementation I left out for sake of brevity)
[<EntryPoint>]
let main args =
let arglist = args |> List.ofSeq
match arglist with
| target :: "list" :: [] ->
listContent target
| target :: "remove" :: name :: [] ->
removeContent target name
| target :: "add" :: name :: [] ->
addContent target name
| target :: "addall" :: names ->
for name in names do
addContent target name
| _ -> // catches cases not covered above
printfn "Usage : "
// print usage message
How about Active Patterns for parsing individual commands, returning an Option indicating None on failure and Some with the content of recovered parameters, possibly nestled in their own dedicated type. Converting "argv" to "string list" is merely a convenience act due to the convenience of list syntax in F#. Note: there are a lot of type annotations here that are typically unnecessary.
type Cmd1Parms = ....
type Cmd2Parms = ....
let performCmd1 cmd1Parms = ...
let performCmd2 cmd2Parms = ...
let commandNotFound argL = ...
let (|ParseForCmd1|_|) argL : Cmd1Parms option = ....
let (|ParseForCmd2|_|) argL : Cmd2Parms option = ....
[<EntryPoint>]
let main argv =
let argL = List.ofSeq<string> argv
match argL with
| ParseForCmd1 cmd1Parms -> performCmd1 cmd1Parms
| ParseForCmd2 cmd2Parms -> performCmd2 cmd2Parms
| _ -> commandNotFound argL
Related
I am trying to obtain a specific value from one list with a multiple recursion so I have :
type PowerSystem =
| System of string * int
| Junction of string * List<PowerSystem>
let Starship =
Junction("Core",
[
Junction("Users",
[
System("Main Computer",-10);
System("Library Computer",-10);
Junction("Defence",)]
let rec JunctionPath (pSystem:PowerSystem) =
match pSystem with
| Junction(name,aList) -> SplitList2 aList
| System(name,aNumber) -> [name]
and SplitList2 list =
match list with
| [] -> printfn "%A" []
| head::tail -> printfn "%A" List.filter (fun e->e="Port Phasers" (JunctionPath head)#(SplitList2 tail))
JunctionPath Starship
I get error FS0001: The type ''a -> string list' does not match the type 'unit'
I want to get the name of junctions when the system is called Main Computer but I can't call other functions. I tryed in different ways to get those values but I can't found the way. Thanks in advance
SplitList2 only has the side effect of printfn hence returns unit ().
In JunctionPath the first branch returns a list of string - [name] - and the second a unit due to calling SplitList2. This is not allowed as both (in general all) pattern match branches should return the same type. This is why you get your error "error FS0001: The type ''a -> string list' does not match the type 'unit'"
(It would be clearer if you, in future, note the line references in the error as that points to the problematic line).
Now in answer to your last point:
I want to get the name of junctions when the system is called Main
Computer but I can't call other functions. I tryed in different ways
to get those values but I can't found the way.
this does not make sense since what is "Port Phasers"? Anyway, without testing, I have modified SplitList2 to return a list on either branch. Then we pipe the result of JunctionPath into a printfn. However the logic of the line List.filter (fun e->e="Port Phasers" (JunctionPath head)#(SplitList2 tail)) still does not make sense, so I updated it as I think you meant it (although you should have had another error for this?). (This is not tail recursive and can be improved but one step at a time)
type PowerSystem =
| System of string * int
| Junction of string * List<PowerSystem>
let Starship =
Junction("Core",
[
Junction("Users",
[
System("Main Computer",-10);
System("Library Computer",-10);
Junction("Defence",)]
let rec JunctionPath (pSystem:PowerSystem) =
match pSystem with
| Junction(name,aList) -> SplitList2 aList
| System(name,aNumber) -> [name]
and SplitList2 list =
match list with
| [] -> []
| head::tail -> List.filter (fun e->e="Port Phasers") (JunctionPath head)#(SplitList2 tail))
JunctionPath Starship |> printfn "%A%
I'm de-serializing some mappings from JSON and later on I need to pattern match based on a string field of the de-serialized types like this:
let mappings = getWorkItemMappings
let result =
workItemMappings
|> Seq.find (fun (m: WorkItemMapping) -> m.Uuid = workTime.workItemUuid)
match mapping.Name with
Even if I complete the pattern match for all cases I still get Incomplete pattern matches on this expression.. Which is obvious to me due to the string type of the Name field.
Is there a way tell the compiler which values for the Name field are available?.
I think I could create a union type for the possible mapping types and try to de-serialize the JSON to this union type but I would like to if there's another option.
If you are pattern matching on a string value, the compiler has no static guarantee that it will only have certain values, because it is always possible to construct a string of a different value. The fact that it comes from JSON does not help - you may always have an invalid JSON.
The best option is to add a default case which throws a custom descriptive exception. Either one that you handle somewhere else (to indicate that the JSON file was invalid) or (if you check the validity elsewhere) something like this:
let parseFood f =
match f with
| "burger" -> 1
| "pizza" -> 2
| _ -> raise(invalidArg "f" $"Expected burger or pizza but got {f}")
Note that the F# compiler is very cautious. It does not even let you handle enum values using pattern matching, because under the cover, there are ways of creating invalid enum values! For example:
type Foo =
| A = 1
let f (a:Foo) =
match a with
| Foo.A -> 0
warning FS0104: Enums may take values outside known cases. For example, the value 'enum (0)' may indicate a case not covered by the pattern(s).
Very hard to understand what you're asking. Maybe this snippet can be of help. It demos how literal string constants can be used in pattern matching, and reused in functions. This gives some added safety and readability when adding and removing cases. If you prefer not to serialize a DU directly, then perhaps this is useful as part of the solution.
type MyDu =
| A
| B
| C
let [<Literal>] A' = "A"
let [<Literal>] B' = "B"
let [<Literal>] C' = "C"
let strToMyDuOption (s: string) =
match s with
| A' -> Some A
| B' -> Some B
| C'-> Some C
| _ -> None
let strToMyDu (s: string) =
match s with
| A' -> A
| B' -> B
| C'-> C
| s -> failwith $"MyDu case {s} is unknown."
let myDuToStr (x: MyDu) =
match x with
| A -> A'
| B -> B'
| C -> C'
// LINQPad
let dump x = x.Dump()
strToMyDuOption A' |> dump
strToMyDuOption "x" |> dump
myDuToStr A |> dump
I tried to implement this straight forward Maybe monad. So basically the whole expression evaluates to Nothing if one of the middle step is Nothing.
type Maybe<'a> =
| Just of 'a
| Nothing
type MaybeBuilder () =
member this.Combine ((first, second) : Maybe<'a> * Maybe<'b>) : Maybe<'b> =
printfn "Combine called"
match first with
| Nothing -> Nothing
| _ ->
match second with
| Nothing -> Nothing
| _ as a -> a
member this.Zero () = Just ()
member this.Bind((m, f) : Maybe<'a> * ('a -> Maybe<'b>)) =
printfn "Bind called"
match m with
| Nothing -> Nothing
| Just a -> f a
let MaybeMonad = MaybeBuilder()
let foobar =
MaybeMonad {
let! foo = Just "foo"
Just 1
Nothing
}
I expected foobar be translated into Just "foo" >>= fun foo -> Combine(Just 1, Nothing), however Combine wasn't called.
That's not the way the computation expression is expected to be written. Each time you want 'yield a result' you need to add some keyword (return, return!, yield or yield!) on the left side of the expression, in your example I would add a return!:
let foobar =
MaybeMonad {
let! foo = Just "foo"
return! Just 1
return! Nothing
}
But then you need to add its definition to the builder:
member this.ReturnFrom (expr) = expr
then the compiler will ask you to add a Delay method, in your case I think you're looking for something like:
member this.Delay(x) = x()
Almost there, now you have a value restriction, most likely because the Combine you defined doesn't use the same type on both arguments, you can either fix it or just add a type annotation in the return type:
let foobar : Maybe<int> =
MaybeMonad {
let! foo = Just "foo"
return! Just 1
return! Nothing
}
That's it, now you get:
Bind called
Combine called
printed and:
val foobar : Maybe<int> = Nothing
If you want to understand all the details of CEs have a look at this nice article: https://www.microsoft.com/en-us/research/publication/the-f-computation-expression-zoo/
I'd like to generate a type with static functions, that I can then use in functions that become quotations using the ReflectedDefinition attribute. It seems to me a convenient way of translating something into FSharp types, using FSharp to compose the domain of functions and types, and spit the composed code back into its native form, having received the benefit of type checking, VS intellisense, higher order functions, etc. To try and get started I have this type provider, largely copy->pasted from various articles
[<TypeProvider>]
type CSoundTypeProvider(config: TypeProviderConfig) as this =
inherit ProvidedTypes.TypeProviderForNamespaces()
let namespaceName = "TestNamespace"
let thisAssembly = Assembly.GetExecutingAssembly()
// config.
let intType = typeof<int>
let providedParam = ProvidedTypes.ProvidedParameter("prm", intType)
let providedFunction = ProvidedTypes.ProvidedMethod("TestMethod", [providedParam], intType, IsStaticMethod=true
, InvokeCode = fun args ->
// The 'args' parameter represents expressions that give us access to the
// instance on which the method is invoked and other parameters (if there are more)
let instance = args.[0]
// Now we can return quotation representing a call to MethodInfo 'p' with 'instance'
instance)
let csoundProvidedWrapper = ProvidedTypes.ProvidedTypeDefinition(thisAssembly, namespaceName, "TestType", None)
do csoundProvidedWrapper.AddMember(providedFunction)
do this.AddNamespace(namespaceName, [csoundProvidedWrapper])
And testing it with this reflected definition:
[<ReflectedDefinition>]
let myfn i j =
let k = i * j
let x = k + 2
let f = TestNamespace.TestType.TestMethod k
let ret = f + 2
ret
I'm parsing the reflected definition like this:
<# myfn #> |> println
println is a function (copied from another article) that has many of the active patterns for parsing quotations like Patterns.Call(None, DerivedPatterns.MethodWithReflectedDefinition(n), expList), which gets me an experssion tree of all of the code, except for the provided static method. Is what I'm trying to do even possible? If so, what's the active pattern I've likely missed from my println function here:
let println expr =
let rec print expr = match expr with
| Patterns.Application(expr1, expr2) ->
// Function application.
print expr1
printf " "
print expr2
| Patterns.Call(None, DerivedPatterns.MethodWithReflectedDefinition(n), expList) ->
print n
| Patterns.Call(exprOpt, methodInfo, exprList) ->
// Method or module function call.
match exprOpt with
| Some expr -> print expr
| None -> printf "%s" methodInfo.DeclaringType.Name
printf ".%s(" methodInfo.Name
if (exprList.IsEmpty) then printf ")" else
print exprList.Head
for expr in exprList.Tail do
printf ","
print expr
printf ")"
| DerivedPatterns.Int32(n) ->
printf "%d" n
| Patterns.Lambda(param, body) ->
// Lambda expression.
printf "fun (%s:%s) -> " param.Name (param.Type.ToString())
print body
| Patterns.Let(var, expr1, expr2) ->
// Let binding.
if (var.IsMutable) then
printf "let mutable %s = " var.Name
else
printf "let %s = " var.Name
print expr1
printf " in "
print expr2
| Patterns.PropertyGet(_, propOrValInfo, _) ->
printf "%s" propOrValInfo.Name
| DerivedPatterns.String(str) ->
printf "%s" str
| Patterns.Value(value, typ) ->
printf "%s" (value.ToString())
| Patterns.Var(var) ->
printf "%s" var.Name
| _ -> printf "%s" (expr.ToString())
print expr
If I can't do this, what approach would you recommend for generating FSharp definitions that I can use in quotations? I've largely been influenced by the FunScript project, but hoping to avoid the step where it seems each Typescript definition has to get compiled into a seperate DLL.
Most of the type provider demos out there use erased type providers which do not produce actual .NET types. When you use an erased type provider, the generated methods are erased and replaced with the code you provided in the method's InvokeCode.
Let's say you have a method Foo that erases to someFunc with "Foo" as an argument:
myObj.Foo() ~> someFunc(myObj, "Foo")
In quotations, you will also see the erased version (both in direct <# .. #> and in ReflectedDefinition):
<# myObj.Foo() #> ~> <# someFunc(myObj, "Foo") #>
The way FunScript type provider works is that it generates some dummy code that contains the function name so that it can then generate the corresponding JavaScript. Say you have:
<# win.Alert(arg) #> ~> <# invokeFunction("alert", win, arg) #>
To do the same sort of thing, you'll need to define a function like invokeFunction and to generate appropriate InvokeCode. Then you can look for calls to invokeFunction in the generated quotation and do whatever special thing you need to do there. It is a bit hard to see what exactly are you trying to do, but this should at least point you in the right direction.
Below I post a fragment of my F# program that causes problems.
...
match words with
| name :: "of" :: context :: "=" :: value :: _ when Double.TryParse(value) |> fst ->
let var = new FuzzyVariable(name, context, Double.Parse value)
fuzzyVars <- var :: fuzzyVars
In line:
fuzzyVars <- var :: fuzzyVars
I get "Type unit does not have null as a proper value" error. I am fairly new to F# programming and I don't quite know what might be causing this issue.
fuzzyVars is of type FuzzyVariable list. FuzzyVariable is a custom defined type.
EDIT.
As #Tomas Petricek pointed out there was a line in my code that returned null:
| [] -> null
My intention was to ignore value of the match. The proper way to do it is:
| [] -> ()
After that change everything works fine.
As already mentioned, the problem is not in the piece of code you posted - it looks like some other part of your program makes the F# compiler think that the expression fuzzyVars <- var :: fuzzyVars should have a type that admits null (but that's not the case, because it returns unit).
I was able to get the same error by writing:
open System
type FuzzyVariable(a:string, b:string, c:float) =
member x.A = ""
let mutable fuzzyVars : (FuzzyVariable list) = []
let words = [null; "of"; "context"; "="; "5"]
And the main part:
null = (match words with
| name :: "of" :: context :: "=" :: value :: _ when Double.TryParse(value) |> fst ->
let var = new FuzzyVariable(name, context, Double.Parse value)
fuzzyVars <- var :: fuzzyVars)
You probably do not have something like this in your code :-) but perhaps the error message might give you a pointer to where the null comes from. Here, I get:
error FS0001: The type 'unit' does not have 'null' as a proper value. See also C:\Users\Tomas\AppData\Local\Temp\~vs648E.fsx(8,0)-(8,4).
And the code on line 8 between character 0 and 4 is the null value! So perhaps check out whether the error message gives you some more information? Or try looking for null elsewhere in your code... (It might be also caused by some unexpected indentation.)
I tried this and it compiled just fine:
open System
type test = {Name:string}
let mutable fuzzyVars : (test list) = []
match [null; "of"; "context"; "="; "5"] with
| name :: "of" :: context :: "=" :: value :: _ when Double.TryParse(value) |> fst ->
let var = {Name=name}
fuzzyVars <- var :: fuzzyVars
| a -> a |> ignore
The problem isn't in this section of code.