In F# I have an optional function with signature type func= (string-> string list) option. How to pass an optional string parameter of signature type param= string option to the func method.
Is there any way to do this without using nested if else.
I didn't notice right away that the function itself is also wrapped in option. I'll keep the original answer to misread question below for reference, but here's the answer for the actual question:
First, you still have to decide what you want to do when there is no function and/or no string (see below). Once you have the decision, you can just match on the tuple:
let resultingList =
match func, param with
| Some f, Some p -> f p // When both are present, apply
| _ -> [] // Otherwise, return empty list
Here, I collapse all cases when either one or both are absent. But if you need different behavior for these cases, you can enumerate all or some of them explicitly:
let resultingList =
match func, param with
| Some f, Some p -> f p
| Some f, None -> f ""
| None, Some _ -> [ "there was no function" ]
| None, None -> [ "there was no string AND no function" ]
The original answer to the misread question
The type string option means "There may be a string here, or not". So the first question to ask yourself is: what do you want to do when there is no string? I can't help you decide on this one, because this depends on your larger problem (i.e. what you are ultimately trying to do).
The function string -> string list takes a string and returns a string list. So if you have a string, then you get back a string list. But what if you don't have a string? Should you get back a "nothing" (i.e. "there is no list", just like "there was no string" to begin with)? Or should you get an empty list? Or should you get some predefined value?
For the sake of gradual learning, let's just assume that, when there is no string, you want to get back an empty list. To do this, use pattern matching:
let resultingList =
match param with
| Some s -> func s
| None -> []
This program literally says: "if there is a string, call that string s and apply func to it; and if there is no string, return an empty list".
Same thing would apply to getting some other predefined list instead of the empty one:
let resultingList =
match param with
| Some s -> func s
| None -> [ "oopsie, there was no string!" ]
But if you want the other option - the "there is no list" one, - then your resulting value will need to be string list option, just like your input is string option. The option type has two constructors: Some to create values that are present, and None to denote "there is no spoon value".
let maybeResultingList =
match param with
| Some s -> Some (func s)
| None -> None
This literally says: "if there is a string, call it s, apply func to it, and wrap the result as Some, which means "there is a list here"; and if there is no string, just return None, which means "there is no list".
As luck would have it, this "apply function to the value, unless it's not there" operation is so common that there is a standard library function for it. It's called Option.map:
let maybeResultingList = Option.map func param
Another interesting way to look at it is this: first we pass func to Option.map and get back another function, and then we apply that function to param:
let maybeFunc = Option.map func
let maybeResultingList = maybeFunc param
This way of looking at it turns out to be very useful in practice. The venerable Scott Wlaschin has a totally kick-ass series on the concept. Check it out here: https://fsharpforfunandprofit.com/posts/elevated-world/
Related
Right now I have two types:
type Rating = (String, Int)
type Film = (String, String, Int, [Rating])
I have a file that has this data in it:
"Blade Runner"
"Ridley Scott"
1982
("Amy",5), ("Bill",8), ("Ian",7), ("Kevin",9), ("Emma",4), ("Sam",7), ("Megan",4)
"The Fly"
"David Cronenberg"
1986
("Megan",4), ("Fred",7), ("Chris",5), ("Ian",0), ("Amy",6)
How can I look through then file storing all of the entries into something like FilmDatabase = [Film] ?
Haskell provides a unique way of sketching out your approach. Begin with what you know
module Main where
type Rating = (String, Int)
type Film = (String, String, Int, [Rating])
main :: IO ()
main = do
films <- readFilms "ratings.dat"
print films
Attempting to load this program into ghci will produce
films.hs:8:12: Not in scope: `readFilms'
It needs to know what readFilms is, so add just enough code to keep moving.
readFilms = undefined
It is a function that should do something related to Film data. Reload this code (with the :reload command or :r for short) to get
films.hs:9:3:
Ambiguous type variable `a0' in the constraint:
(Show a0) arising from the use of `print'
...
The type of print is
Prelude> :t print
print :: Show a => a -> IO ()
In other words, print takes a single argument that, informally, knows how to show itself (that is, convert its contents to a string) and creates an I/O action that when executed outputs that string. It’s more-or-less how you expect print to work:
Prelude> print 3
3
Prelude> print "hi"
"hi"
We know that we want to print the Film data from the file, but, although good, ghc can’t read our minds. But after adding a type hint
readFilms :: FilePath -> Film
readFilms = undefined
we get a new error.
films.hs:8:12:
Couldn't match expected type `IO t0'
with actual type `(String, String, Int, [Rating])'
Expected type: IO t0
Actual type: Film
In the return type of a call of `readFilms'
In a stmt of a 'do' expression: films <- readFilms "ratings.dat"
The error tells you that the compiler is confused about your story. You said readFilms should give it back a Film, but the way you called it in main, the computer should have to first perform some I/O and then give back Film data.
In Haskell, this is the difference between a pure string, say "JamieB", and a side effect, say reading your input from the keyboard after prompting you to input your Stack Overflow username.
So now we know we can sketch readFilms as
readFilms :: FilePath -> IO Film
readFilms = undefined
and the code compiles! (But we can’t yet run it.)
To dig down another layer, pretend that the name of a single movie is the only data in ratings.dat and put placeholders everywhere else to keep the typechecker happy.
readFilms :: FilePath -> IO Film
readFilms path = do
alldata <- readFile path
return (alldata, "", 0, [])
This version compiles, and you can even run it by entering main at the ghci prompt.
In dave4420’s answer are great hints about other functions to use. Think of the method above as putting together a jigsaw puzzle where the individual pieces are functions. For your program to be correct, all the types must fit together. You can make progress toward your final working program by taking little babysteps as above, and the typechecker will let you know if you have a mistake in your sketch.
Things to figure out:
How do you convert the whole blob of input to individual lines?
How do you figure out whether the line your program is examining is a title, a director, and so on?
How do you convert the year in your file (a String) to an Int to cooperate with your definition of Film?
How do you skip blank or empty lines?
How do you make readFilms accumulate and return a list of Film data?
Is this homework?
You might find these functions useful:
readFile :: FilePath -> IO String
lines :: String -> [String]
break :: (a -> Bool) -> [a] -> ([a], [a])
dropWhile :: (a -> Bool) -> [a] -> [a]
null :: [a] -> Bool
read :: Read a => String -> a
Remember that String is the same as [Char].
Some clues:
dropWhile null will get rid of empty lines from the start of a list
break null will split a list into the leading run of non-empty lines, and the rest of the list
Haskell has a great way of using the types to find the right function. For instance: In Gregs answer, he wants you to figure out (among other things) how to convert the year of the film from a String to an Int. Well, you need a function. What should be the type of that function? It takes a String and returns an Int, so the type should be String -> Int. Once you have that, go to Hoogle and enter that type. This will give you a list of functions with similar types. The function you need actually has a slightly different type - Read a => String -> a - so it is a bit down the list, but guessing a type and then scanning the resulting list is often a very useful strategy.
I love the simplicity of types like
type Code = Code of string
But I would like to put some restrictions on string (in this case - do not allow empty of spaces-only strings). Something like
type nonemptystring = ???
type Code = Code of nonemptystring
How do I define this type in F# idiomatic way? I know I can make it a class with constructor or a restricted module with factory function, but is there an easy way?
A string is essentially a sequence of char values (in Haskell, BTW, String is a type alias for [Char]). A more general question, then, would be if it's possible to statically declare a list as having a given size.
Such a language feature is know as Dependent Types, and F# doesn't have it. The short answer, therefore, is that this is not possible to do in a declarative fashion.
The easiest, and probably also most idiomatic, way, then, would be to define Code as a single-case Discriminated Union:
type Code = Code of string
In the module that defines Code, you'd also define a function that clients can use to create Code values:
let tryCreateCode candidate =
if System.String.IsNullOrWhiteSpace candidate
then None
else Some (Code candidate)
This function contains the run-time logic that prevents clients from creating empty Code values:
> tryCreateCode "foo";;
val it : Code option = Some (Code "foo")
> tryCreateCode "";;
val it : Code option = None
> tryCreateCode " ";;
val it : Code option = None
What prevents a client from creating an invalid Code value, then? For example, wouldn't a client be able to circumvent the tryCreateCode function and simply write Code ""?
This is where signature files come in. You create a signature file (.fsi), and in that declare types and functions like this:
type Code
val tryCreateCode : string -> Code option
Here, the Code type is declared, but its 'constructor' isn't. This means that you can't directly create values of this types. This, for example, doesn't compile:
Code ""
The error given is:
error FS0039: The value, constructor, namespace or type 'Code' is not defined
The only way to create a Code value is to use the tryCreateCode function.
As given here, you can no longer access the underlying string value of Code, unless you also provide a function for that:
let toString (Code x) = x
and declare it in the same .fsi file as above:
val toString : Code -> string
That may look like a lot of work, but is really only six lines of code, and three lines of type declaration (in the .fsi file).
Unfortunately there isn't convenient syntax for declaring a restricted subset of types but I would leverage active patterns to do this. As you rightly say, you can make a type and check it's validity when you construct it:
/// String type which can't be null or whitespace
type FullString (string) =
let string =
match (System.String.IsNullOrWhiteSpace string) with
|true -> invalidArg "string" "string cannot be null or whitespace"
|false -> string
member this.String = string
Now, constructing this type naively may throw runtime exceptions and we don't want that! So let's use active patterns:
let (|FullStr|WhitespaceStr|NullStr|) (str : string) =
match str with
|null -> NullStr
|str when System.String.IsNullOrWhiteSpace str -> WhitespaceStr
|str -> FullStr(FullString(str))
Now we have something that we can use with pattern matching syntax to build our FullStrings. This function is safe at runtime because we only create a FullString if we're in the valid case.
You can use it like this:
let printString str =
match str with
|NullStr -> printfn "The string is null"
|WhitespaceStr -> printfn "The string is whitespace"
|FullStr fstr -> printfn "The string is %s" (fstr.String)
I am trying to experiment in F# for one of the utility tools we need, wherein we want to trawl through a folder of xml files and look for a particular tag. If found then insert another similar tag alongwith it. Finally, output all the filenames for which such additional tags have been inserted. But am getting a compilation error, of which I am not able to make a lot of sense.
let configFile =
Directory.GetFiles(Path.Combine("rootdir", "relativepath"), #"*.xml")
|> Seq.map(fun configFileName ->
let xmlNavigator = XPathDocument(configFileName).CreateNavigator()
let node = xmlNavigator.SelectSingleNode(#"Product/ABc[#type='xyz']")
match node with
| null -> "not configuration present"
| _ ->
let nodeAppender() = node.InsertAfter("<Risk type=""abc1"" methodology=""xyz1""/>")
let parentNode = node.SelectAncestors(XPathNodeType.Root, false)
parentNode.Current.OuterXml)
|> Seq.iter (printfn "%s")
The compilation error is as below:
This value is not a function and cannot be applied
Your string is escaped improperly. It should be:
node.InsertAfter("<Risk type=\"abc1\" methodology=\"xyz1\"/>")
EDIT: Apparently I was typing this as Brian posted his answer. Either escaping each quote char or prefixing with # as-is will work.
It would help to point out what line/column the error location is at.
At a glance, in nodeAppender, it looks like you left off the # on the string literal, which means it is five strings in a row (rather than one string with escaped quotes), which may be the cause of the error.
This compiler like:
let test Xf Yf = Xf + Yf
This compiler no like:
let test Xfd Yfd = Xfd + Yfd
Warning:
Uppercase variable identifiers should not generally be used in patterns, and may indicate a misspelt pattern name.
Maybe I'm not googling properly, but I haven't managed to track down anything which explains why this is the case for function parameters...
I agree that this error message looks a bit mysterious, but there is a good motivation for it. According to the F# naming guidelines, cases of discriminated unions should be named using PascalCase and the compiler is trying to make sure that you don't accidentally misspell name of a case in pattern matching.
For example, if you have the following union:
type Side =
| Left
| Right
You could write the following function that prints "ok" when the argument is Left and "wrong!" otherwise:
let foo a =
match a with
| Lef -> printfn "ok"
| _ -> printfn "wrong!"
There is a typo in the code - I wrote just Lef - but the code is still valid, because Lef can be interpreted as a new variable and so the matching assigns whatever side to Lef and always runs the first case. The warning about uppercase identifiers helps to avoid this.
F# tries to enforce case rules for active patterns - consider what does this code do
let f X =
match X with
|X -> 1
|_ -> 2
This is quite confusing. Also, function parameters are similar to patterns, you can do
let f (a,b,_) = a,b
for example. Not quite sure why the third letter triggers the warning though
Let's say I have a significant class hierarchy:
Tag
ControlFlowTag
IfTag
ForTag
JumpTag
HTMLTag
DivTag
and I want to make a list interspersed with these and strings.
let MyList = [tagA, tagB, "some text", tagC]
and I thought I could discriminated union it
type Node =
| Tag of Tag
| String of String
let MyList: list<Node> = [tagA, tagB, "some text", tagC]
but alas, it doesn't work without
let MyList: list<Node> = [Tag tagA, Tag tagB, String "some text", Tag tagC]
Obviously the Tag and String described in Node are orthogonal and separate from the existing Tag/String classes. Mousing over gives me the types as Node.Tag and Node.String, which isn't what I want.
What I have now is a function t which creates a StringTag which inherits from Tag, giving me
let MyList : list<Tag> = [tagA, tagB, t"some text", tagC]
which is pretty nice, but the extra t adds to the visual noise. What I actually want is a strongly typed "list of two different types" which I could work with using match statements. I thought that was the point of Discriminated Unions, but their inability to use existing type hierarchies is a problem, since the existing hierarchy (in this case Tag) is complex enough I think a full OO-inheritence approach to that subset of types is clearer than a pure Discriminated Union approach
One option is to just make it a list of obj and cast everything before/during the match, but that's not really very nice. Are there any other approaches?
If you had two different DUs, say
type Node =
| Tag of Tag
| String of String
and
type Foo =
| Bar of Tag
| Name of String
how would the compiler know of which type the following list is?
[tagA; tagB; "some text"; tagC]
As svick said, the discriminator is necessary. If you use classes instead you'll need to upcast to the base type, so I'm not sure you save on keystrokes.
If you're working with dictionaries, here is a nice option to reduce the syntactic noise of boxing. Maybe you can do something similar for lists.
I don't know how helpful this is, but you can use Active Patterns to match a class hierarchy in a DU-like fashion if appropriate.
[<AbstractClass>]
type Animal() =
abstract Talk : string
type Cat() =
inherit Animal()
override this.Talk = "Meow"
type Dog() =
inherit Animal()
override this.Talk = "Woof"
type SuperCat(s) =
inherit Cat()
override this.Talk = s
let animals : list<Animal> =
[Dog(); Cat(); SuperCat("MEOW")]
let (|SCSaid|_|) (a:Animal) = // Active Pattern
match a with
| :? SuperCat as sc -> Some sc.Talk
| _ -> None
for a in animals do
match a with
| :? Dog -> printfn "dog"
| SCSaid s -> printfn "SuperCat said %s" s // looks like DU
| _ -> printfn "other"
//dog
//other
//SuperCat said MEOW
Discriminated unions are just that – discriminated (unlike e.g. C unions). That means you have to always add the discriminator.
If this were C#, I would think about having an implicit conversion from string to StringTag. But since F# doesn't support implicit conversions, I think the second approach is your best bet. Although I would make the function's name more descriptive, not just t. Most of the time, it's better to write code that's easy to read, not code that's easy to write.