0

I tried to execute the following function in multiple ways, but it was always in error. So how do i execute constructQuery :

type PersonName =
| FirstOnly of string
| LastOnly of string
| FirstLast of string * string

let constructQuery personName = 
    match personName with
    | FirstOnly(firstName) -> printf "May I call you %s?" firstName
    | LastOnly(lastName) -> printf "Are you Mr. or Ms. %s?" lastName
    | FirstLast(firstName, lastName) -> printf "Are you %s %s?" firstName lastName

-edit-.

I have tried calling the function like this:

constructQuery "himekami"
constructQuery ("himekami" : PersonName.FirstOnly)
constructQuery PersonName.FirstOnly("himekami")

and error produced was like this :

Analyzer.fs(12,17): error FS0001: This expression was expected to have type
PersonName    

but here has type string

This is because i don't quite understand the way "type" in F# works.

himekami
  • 1,419
  • 3
  • 13
  • 21

1 Answers1

7

I guess the question is how to construct PersonName objects (as those are the input for your function).

This is quite easy - just use one of the constructors FirstOnly, LastOnly or FirstLast:

let firstOnly = FirstOnly "Tom"
let lastOnly  = LastOnly "Hengs"
let firstLast = FirstLast ("Tom", "Hengs")

and you can use them like this:

constructQuery firstOnly
constructQuery lastOnly
constructQuery firstLast

background

You see PersonName is an algebraic datatype with it's 3 constructors and the constructQuery matches it's only parameter for them. In VisualStudio (or MonoDevelop) you should be able to get a tooltip with the types for each part of this - you should do this all the times, because types are a big part understanding.

other Ways to call this function

you can use the pipe-operator |> and do this in one go:

FirstOnly "Tom"
|> constructQuery

of course you can use it without this operator too:

constructQuery (LastOnly "Hengs")

but here you need the parens because without them you would plug the function LastOnly (and yes this is a function String -> PersonName) into the function constructQuery and then apply the string like this:

constructQuery LastOnly "Hengs" = (constructQuery LastOnly) "Hengs" // error: compiler complains about mismatched types

because LastOnly is really a function (sadly this is not the case for C# constructors) you can do cool stuff like this too:

// Pipes all the way
"Hengs" |> LastOnly |> constructQuery

// this is another function String -> PersonName
let constructFromLastOnly = LastOnly >> constructQuery

// you can call it like this
constructFromLastOnly "Hengs"

// etc. ... imagine

another alternative is to use the backwards pipe <| (not so idiomatic):

constructQuery <| LastOnly "Hengs"

where you don't need the parens either

Remarks

I would encourage you (even if the compiler don't really needs it) to give the types for top-level functions (thouse you want to use from other parts of your program):

let constructQuery (personName : PersonName) : () =
   match personName with
   // ...

Did you see that you don't really need personName besides the call to match? This is so common that there is another/shorter way to write this:

let constructQuery = function
    | FirstOnly(firstName) -> printf "May I call you %s?" firstName
    | LastOnly(lastName) -> printf "Are you Mr. or Ms. %s?" lastName
    | FirstLast(firstName, lastName) -> printf "Are you %s %s?" firstName lastName

or with a signature:

let constructQuery : PersonName -> () = 
    function
    | FirstOnly(firstName) -> printf "May I call you %s?" firstName
    | LastOnly(lastName) -> printf "Are you Mr. or Ms. %s?" lastName
    | FirstLast(firstName, lastName) -> printf "Are you %s %s?" firstName lastName

And finally - I don't like the fact that you mix printf in there. See a function returning unit () is never pure and you mix concerns. Why not like this:

let constructQuery : PersonName -> string = 
    function
    | FirstOnly(firstName) -> sprintf "May I call you %s?" firstName
    | LastOnly(lastName) -> sprintf "Are you Mr. or Ms. %s?" lastName
    | FirstLast(firstName, lastName) -> sprintf "Are you %s %s?" firstName lastName

and use it like this:

FirstOnly "Tom"
|> constructQuery
|> Console.WriteLine

so you can reuse it for example in a WPF app or in a logging scenario where the console is not avaiable.

Random Dev
  • 51,810
  • 9
  • 92
  • 119