F# Matching mutable object (string) - f#

Here is the full code so far:
module clean
#light
open System
open System.IO
let pause() = Console.ReadLine()
let drive = System.IO.Directory.GetDirectoryRoot(System.IO.Directory.GetCurrentDirectory())
printfn "You're using the %s drive.\n\n" drive
let game1 = "Assassin's Creed"
let game2 = "Crysis"
let game3 = "Mass Effect"
let local1 = "\%APPDATA\%\\Ubisoft\\Assassin's Creed\\Saved Games\\"
let local2 = "\%USERPROFILE\%\\Documents\\My Games\\Crysis\\SaveGames\\"
let local3 = "\%USERPROFILE\%\\Documents\\BioWare\\Mass Effect\\Save\\"
let roam1 = drive + "Saves\\Abraxas\\" + game1 + "\\"
let roam2 = drive + "Saves\\Abraxas\\" + game2 + "\\"
let roam3 = drive + "Saves\\Abraxas\\" + game3 + "\\"
let rec getGame() =
printfn "Which Game?\n\n 1.%s\n 2.%s\n 3.%s\n\n" game1 game2 game3
match Int32.TryParse(stdin.ReadLine()) with
| true,1 -> game1
| true,2 -> game2
| true,3 -> game3
| _ ->
printfn "You did not enter a valid choice."
let _ = pause()
Console.Clear()
getGame()
let mutable gameprint = getGame()
printf "You have chosen %s\n\n" gameprint
let roaming =
match gameprint with
| game1 -> roam1
| game2 -> roam2
| game3 -> roam3
| _ -> "test"
printf "Roaming set to %s\n\n" roaming
let local =
match gameprint with
| game1 -> local1
| game2 -> local2
| game3 -> local3
| _ -> "test"
printf "Local set to %s\n\n" local
printf "Check gameprint %s" gameprint
In the section that sets the roaming and local objects, it is telling me that it will never match with anything other than 'game1'.
I did the 'printf' to check before and after matching with the local and roaming objects... The gameprint shows correctly in both of the printf commands, but doesn't match to anything other than game1... I'm not sure where I made the mistake.

Two things.
In F#, bindings can be shadowed. In particular, within your match, when you use game1, game2, and game3 in patterns, you are actually declaring new bindings with these names. Therefore, the first pattern will always match, and will just assign whatever value you are trying to match against it to the new binding game1 before evaluating the right hand side.
One way to work around this is to declare your gameN bindings with the [<Literal>] attribute (but note that they must also start with capital letters to work as constants):
[<Literal>]
let Game1 = "Assassin's Creed"
Now you can use Game1 in a pattern match and it will work as you expect.
You may be aware of this, but you're not actually updating the gameprint binding anywhere anyway, so it's going to be set to the same value throughout your program and there is no point to its being mutable.

See F# matching with two values for an explanation.
When comparing against a few non-literal values, I'd just use an if-then-else
if gameprint = game1 then ...
elif gameprint = game2 then ...
...

Maybe something a little more like this would allow you to make it more extensible (if you populate the list of games at runtime) ... sorry the codes a little rushed I'm trying to get ready for work:
open System
type Game = {Title:string; local:string; roam:string}
let game1 = {
Title= "Assassin's Creed";
local = "\%APPDATA\%\\Ubisoft\\Assassin's Creed\\Saved Games\\";
roam = "Saves\\Abraxas\\\Assassin's Creed\\"
}
let game2 = {
Title= "Crysis";
local = "\%USERPROFILE\%\\Documents\\My Games\\Crysis\\SaveGames\\";
roam = "Saves\\Abraxas\\\Crysis\\"
}
let games = [game1; game2]
let printGamelListItem i g = printfn "%i: %s" i g.Title
let printChoice() =
printfn "Which Game?\n"
games |> List.fold (fun acc g ->
printGamelListItem acc g
acc+1) 1
|> ignore
let rec getGame() =
printChoice()
match Int32.TryParse(Console.ReadLine()) with
|true, x when x <= games.Length -> games.[x-1]
| _ ->
printfn "You did not enter a valid choice."
let _ = Console.ReadLine()
Console.Clear()
getGame()
let selection = getGame()
printfn "Roaming set to: %s" (selection.roam)
printfn "Local set to: %s" (selection.local)

Related

Expression was expected to have a type but here has type String

It's my third time today running and this buzz word functional language F# is driving me mad yet when I get certain bits right it feels good
earlier I had a problem with recursive loop someone suggested a way forward and now I am getting the above error, the problem is I know my type is going to be a string so why is the compiler complaining?
Aim was to put 12 weeks of learning into practice so I wanted to work on basic chat bot so far I can hold a basic level conversation, however, there are few things that are still out of my scope for example
why can't I call my type? IUserError to pass user input and check if they have said something that is not in my subject list then to response back with invalid input.
My other issue is to keep things simple I want to convert all input into lowercase string this is also proven to be a challenge.
and then there is numeric at some stage of the conversation user gets to room location for some reason I can find a way other than to do this check.
there is very few tutorial online and the book I am using does not explain things well. I am pleased with what I have achieved so far
if someone can explain where I am going wrong with this because in C# this would have been gone and dusted.:(
here is my whole code feel free to discuss:
open System
open System.IO
open System.Speech.Synthesis
// required for regular expression
open System.Text.RegularExpressions
// init randomizer
let rand = new Random()
// recursive response function find the first match with a key token
// response back acordingly
// Initialise a new instance of SpeechSynthesizer
let voice (sentence: string) =
use speech = new SpeechSynthesizer(Rate= -3)
//speech.SelectVoice("Microsoft Huihui Desktop")
speech.Speak(sentence)
// define functions of set list campus area
let mpCampusArea = Set.ofList ["Cisco Labs"; "The Bridge"; "Security
Area";
"Mac Labs"; "Open Access"]
//active patterns
//method for checking room number
let chkroom () =
let roomNumbers = seq {
yield 158
yield 123
yield 333 }
printfn "Room not found could it be :"
for items in roomNumbers do
printfn "%A" items
let (|Campuses|None|) users =
if Regex.Match(users,".*(MP?|mp|Curzon|curzon|ParkSide?).*" ).Success
then Campuses
else
None
// Apply Active pattern
let(|Repair|None|) input = // any sentence with broken|break|damaged is
require repair
if Regex.Match(input , ".*(broken?|break|damaged?).*").Success
then Repair
else
None
let (|ParkSide|None|) input =
if Regex.Match(input , ".*(P158|P159|P160).*").Success
then ParkSide
else
None chkroom
let (|RoomLocation|None|) str2 =
if Regex.Match(str2, ".*(158|140|150).*").Success
then RoomLocation
else
None chkroom
// Define an active recognizer for keywords that express salutation.
let (|Bye|Answer|NoSubject|MyGirlFriend|Faulty|None|) input =
match input with
| "goodbye" | "bye" | "go" |"get lost"
-> Bye
| "who" | "how" | "when" |"where"
-> Answer
| "car" |"what" |"name" |"bcu"
-> NoSubject
| "lonely" |"love" | "friendship"
-> MyGirlFriend
| "device" |"software" |"phone"
-> Faulty
| _
-> None
let (|Computer|Other|) input =
match input with
|"goodbye"|"bye"|"go" -> Computer
|_ -> Other
// select possible likely hood response based on random number for hello
subject
// Interact with the user
// Subject faulty software and Hardware
let faulty_response (str:string) =
let x = rand.Next(5)
match x with
| 0 -> "My advice is to restart the software / hardware?"
| 1 -> "My advice is relax it will be sorted."
| 2 -> "My advice is bin your device / software."
| 3 -> "Please throw your software / hardware in the recycle bin"
| 5 -> "Kiss your device / software as this always works for me."
| _ -> ""
let good_bye_response () =
let b = rand.Next(5)
match b with
| 0 -> "Good bye Babe"
| 1 -> "Thank God "
| 2 -> "We have to be positive love BCU"
| 3 -> "Live is beautiful but you are a smelly poo little fella BYE!"
| 4 -> "Good bye and thanks for complainting"
| _ -> ""
let answer_response () =
let x = rand.Next(10)
match x with
| 0 -> "Please go and complait to Waheed Rafiq"
| 1 -> "Please go and see Emmett Cooper"
| 2 -> "So you want me to kick a fuss?"
| 3 -> "What a waste of time"
| 4 -> "Please go and see BCU tech department"
| 5 -> "OMG and so what"
| 6 -> "Jump of the roof it will most likely help us all"
| 7 -> "Let's talk about the toliet shall we"
| 8 -> "why don't you use pattern matching with regular expressions!"
| 9 -> "Speak to the Queen she will mostly likely deals with BCU
complaints"
| _ -> ""
let none_response (str:string) =
let n = rand.Next(10)
match n with
| 0 -> "What would you"+ str + "like to chat about ?"
| 1 -> "I do not understand please ask again !"
| 2 -> "How about you Speak english and I log your helpdesk call yeah?"
| 3 -> "Sorry to hear that. Are you sure you want to complaint ?"
| 4 -> "This is a complaint Chat bot where you log helpdesk calls.
Please Refer to Cortana for her services.!"
| 5 -> "Let just complaint yeah for the sake of complaining ?"
| 6 -> "OKay what is your complaint about ?"
| 7 -> "Are you a human because you certainly do not behave like one!"
| 8 -> "The moon is epic. What is broken ?"
| 9 -> "Do you always complaint? Try logging it like my PC is broken
yeah !"
| _ -> ""
type Day =
| Monday
| Tuesday
| Wednesday
| Thursday
| Friday
| Saturday
| Sunday
let isWeekend x =
match x with
|Saturday |Sunday -> true
|_-> false
// using regular expression to tokenisse line of text
let matchWords = Regex(#"\w+")
let token (text:string) =
text.ToLowerInvariant()
|> matchWords.Matches
// Crossing the stream
type IUserError =
interface
end
type Error = { ErrorMessage:string; ErrorCode:int}
interface IUserError
type Success = { Status:string }
interface IUserError
let error = {ErrorMessage = "Incorrect input please enter a subject
phase";
ErrorCode = 250} :> IUserError
match error with
| :? Error as e -> printfn "Code %i \n Message: %s" e.ErrorCode
e.ErrorMessage
| :? Success -> printfn "Success"
|_ -> failwith "Invalid option"
//printfn "%A" error
//recursive response function
let rec response (token: string) (str: string) =
match token with
| Bye
-> good_bye_response ()
| Answer
-> answer_response ()
| Faulty
-> faulty_response str
| Repair
->
sprintf "%s" "Which Campus is the device in?"
| Campuses
-> sprintf "%s" "Which room is the device in?"
| RoomLocation
-> sprintf "%s" "Your call is log. Do you wish to quit?"
|_ when token.Contains("yes") -> "Okay logging you out"
|_ when token.Contains("no") -> answer_response ()
| NoSubject
-> none_response str
| None when (str.IndexOf(" ") > 0)
-> response (str.Substring(0,str.IndexOf(" ")))
(str.Substring(str.IndexOf(" ")+1))
| None when (str.IndexOf(" ") < 0)
-> response str ""
let rec chat () =
let valueInput = Console.ReadLine()
printf "Helpdesk-BCU Response --> %s \n" (response "" valueInput)
let keepRunning, message = response valueInput
printfn ">> %s" message
if keepRunning then chat()
//let rec chat () =
// if Break = false then
// let valueInput = Console.ReadLine()
// printf "Helpdesk-BCU Response --> %s \n" (response "" valueInput)
// if Break = false then
// chat()
// else
// ChatEnd()
let BCU_response (str: string) =
if (str.IndexOf(" ") > 0) then
response (str.Substring(0,str.IndexOf(" "))) (str.Substring(str.IndexOf("
")+1)) + "\n"
else
response str "" + "\n"
// call back feature for the chatbot
//[<EntryPoint>]
//let main argv = printfn "%A" argv
// Advance expression lamba [ Emmett helps required]
let ifancyHerList =
[
("Sara",1); ("Saima",2); ("Zoe",3); ("Scarlett",4);
("Jennifer",5);("Sandra Bullock",6)
]
let myGirlFriend () =
List.pick (fun funToNight ->
let n = rand.Next(10)
if (snd funToNight) = n
then Some (fst funToNight)
else None
) ifancyHerList
//match myGirlFriend with
//| Some name -> printfn "Your date for tonight is %A lucky fella" name
//| None -> printfn "You don't have a date tonight!"
//
printfn "Welcome to the BCU Complaint Chat Bot"
printf "Please enter your first name -->"
let data = Console.ReadLine()
//let rec complaints n =
// printf "%s what do you want to complain about? -->" data
//complaints()
chat()
printfn "The avaialbe areas at Millennium point are: %A" mpCampusArea
printfn "Which day is Weekend on?"
let x = Console.ReadLine()
0
any helps / pointers / anything as this is driving me nuts
I shall post a link direct to the project file if you wish to download it and have a closer look , much appericate your support.
the link to the project file
I'm not sure using an interface is what you want to do here. You don't have any abstract methods defined for IUserError, but then again maybe you were saving that for later. Also, you have a match with block hanging there with no function.
Here is my interpretation of what you could do:
// Crossing the stream
type Error = { ErrorMessage:string; ErrorCode:int}
type Success = { Status:string }
type UserError =
| Error of Error
| Success of Success
let printResponse (error:UserError) =
match error with
| Error (e) -> printfn "Code %i \n Message: %s" e.ErrorCode e.ErrorMessage
| Success _ -> printfn "Success"
|_ -> failwith "Invalid option"
let error = Error {ErrorMessage = "Incorrect input please enter a subject phase"; ErrorCode = 250}
Using fsi to evaluate printResponse, it should look like this:
> printResponse error;;
Code 250
Message: Incorrect input please enter a subject phase
val it : unit = ()

Pattern matching numeric strings

I have a function that pattern matches its argument, which is a string:
let processLexime lexime
match lexime with
| "abc" -> ...
| "bar" -> ...
| "cat" -> ...
| _ -> ...
This works as expected. However, I'm now trying to extend this by expressing "match a string containing only the following characters". In my specific example, I want anything containing only digits to be matched.
My question is, how can I express this in F#? I'd prefer to do this without any libraries such as FParsec, since I'm mainly doing this for learning purposes.
You can use active patterns: https://msdn.microsoft.com/en-us/library/dd233248.aspx
let (|Integer|_|) (str: string) =
let mutable intvalue = 0
if System.Int32.TryParse(str, &intvalue) then Some(intvalue)
else None
let parseNumeric str =
match str with
| Integer i -> printfn "%d : Integer" i
| _ -> printfn "%s : Not matched." str
One way would be an active pattern
let (|Digits|_|) (s:string) =
s.ToCharArray() |> Array.forall (fun c -> System.Char.IsDigit(c)) |> function |true -> Some(s) |false -> None
then you can do
match "1" with
|Digits(t) -> printf "matched"
I would use regular expressions combined with active patterns. With regular expressions you can easily match digits with \d and active patterns makes the syntax nice inside your match.
open System.Text.RegularExpressions
let (|ParseRegex|_|) regex str =
let m = Regex("^"+regex+"$").Match(str)
if (m.Success) then Some true else None
let Printmatch s =
match s with
| ParseRegex "w+" d -> printfn "only w"
| ParseRegex "(w+|s+)+" d -> printfn "only w and s"
| ParseRegex "\d+" d -> printfn "only digis"
|_ -> printfn "wrong"
[<EntryPoint>]
let main argv =
Printmatch "www"
Printmatch "ssswwswwws"
Printmatch "134554"
Printmatch "1dwd3ddwwd"
0
which prints
only w
only w and s
only digis
wrong

Create Discriminated Union Case from String

I'm trying to create DU cases from strings. The only way I can see doing this is by enumerating over the DU cases via Microsoft.FSharp.Reflection.FSharpType.GetUnionCases and then picking the UnionCase that matches the string (by using .Name) and then making the actual DU case out of that by using FSharpValue.MakeUnion.
Isn't there an easier/more elegant way of doing this? In my scenario I have a DU with a couple of hundred cases for keywords. I have to read the strings (keywords) from a file and make the types out of them. I did some "optimization" by putting the cases into a Map but I was hoping there'd be a better way of doing this.
I have the following, for example:
type Keyword =
| FOO
| BAR
| BAZ
| BLAH
let mkKeywords (file: string) =
use sr = new StreamReader(file)
let caseMap =
FSharpType.GetUnionCases(typeof<Keyword>)
|> Array.map (fun c -> (c.Name, FSharpValue.MakeUnion(c, [||]) :?> Keyword))
|> Map.ofArray
[
while not sr.EndOfStream do
let l = sr.ReadLine().Trim()
match caseMap.TryFind l with
| Some c -> yield c
| None -> failwith <| "Could not find keyword: " + l
]
I found this handy code snippet...
open Microsoft.FSharp.Reflection
let toString (x:'a) =
let (case, _ ) = FSharpValue.GetUnionFields(x, typeof<'a>)
case.Name
let fromString<'a> (s:string) =
match FSharpType.GetUnionCases typeof<'a> |> Array.filter (fun case -> case.Name = s) with
|[|case|] -> Some(FSharpValue.MakeUnion(case,[||]) :?> 'a)
|_ -> None
... which makes it easy to tack on two lines of code to any DU...
type A = X|Y|Z with
override this.ToString() = FSharpUtils.toString this
static member fromString s = FSharpUtils.fromString<A> s
I would use pattern matching like this:
type Keyword =
| FOO
| BAR
| BAZ
| BLAH
let matchKeyword (word:string) : Keyword option =
match word with
| "FOO" -> Some FOO
| "BAR" -> Some BAR
| "BAZ" -> Some BAZ
| "BLAH" -> Some BLAH
| _ -> None
And maybe auto generate the match statement first time using regex in my editor, but only because you have hundreds of cases. But i am not sure if its a better solution then yours.
As the cases have no value, another option is to use enums:
type Keyword =
| FOO = 0
| BAR = 1
| BAZ = 2
| BLAH = 3
let strings = ["FOO";"BAR"]
let keywords =
[for s in strings -> s, Keyword.Parse(typeof<Keyword>, s)]
|> Map.ofList
Then you can simply use Enum.Parse.

How do i write the classic high/low game in F#?

I was reading up on functional languages and i wondered how i would implement 'tries' in a pure functional language. So i decided to try to do it in F#
But i couldnt get half of the basics. I couldnt figure out how to use a random number, how to use return/continue (at first i thought i was doing a multi statement if wrong but it seems like i was doing it right) and i couldnt figure out how to print a number in F# so i did it in the C# way.
Harder problems is the out param in tryparse and i still unsure how i'll do implement tries without using a mutable variable. Maybe some of you guys can tell me how i might correctly implement this
C# code i had to do last week
using System;
namespace CS_Test
{
class Program
{
static void Main(string[] args)
{
var tries = 0;
var answer = new Random().Next(1, 100);
Console.WriteLine("Guess the number between 1 and 100");
while (true)
{
var v = Console.ReadLine();
if (v == "q")
{
Console.WriteLine("you have quit");
return;
}
int n;
var b = Int32.TryParse(v, out n);
if (b == false)
{
Console.WriteLine("This is not a number");
continue;
}
tries++;
if (n == answer)
{
Console.WriteLine("Correct! You win!");
break;
}
else if (n < answer)
Console.WriteLine("Guess higher");
else if (n > answer)
Console.WriteLine("Guess lower");
}
Console.WriteLine("You guess {0} times", tries);
Console.WriteLine("Press enter to exist");
Console.ReadLine();
}
}
}
The very broken and wrong F# code
open System;
let main() =
let tries = 0;
let answer = (new Random()).Next 1, 100
printfn "Guess the number between 1 and 100"
let dummyWhileTrue() =
let v = Console.ReadLine()
if v = "q" then
printfn ("you have quit")
//return
printfn "blah"
//let b = Int32.TryParse(v, out n)
let b = true;
let n = 3
if b = false then
printfn ("This is not a number")
//continue;
//tries++
(*
if n = answer then
printfn ("Correct! You win!")
//break;
elif n < answer then
printfn ("Guess higher")
elif n>answer then
printfn ("Guess lower")
*)
dummyWhileTrue()
(Console.WriteLine("You guess {0} times", tries))
printfn ("Press enter to exist")
Console.ReadLine()
main()
Welcome to F#!
Here's a working program; explanation follows below.
open System
let main() =
let answer = (new Random()).Next(1, 100)
printfn "Guess the number between 1 and 100"
let rec dummyWhileTrue(tries) =
let v = Console.ReadLine()
if v = "q" then
printfn "you have quit"
0
else
printfn "blah"
let mutable n = 0
let b = Int32.TryParse(v, &n)
if b = false then
printfn "This is not a number"
dummyWhileTrue(tries)
elif n = answer then
printfn "Correct! You win!"
tries
elif n < answer then
printfn "Guess higher"
dummyWhileTrue(tries+1)
else // n>answer
printfn "Guess lower"
dummyWhileTrue(tries+1)
let tries = dummyWhileTrue(1)
printfn "You guess %d times" tries
printfn "Press enter to exit"
Console.ReadLine() |> ignore
main()
A number of things...
If you're calling methods with multiple arguments (like Random.Next), use parens around the args (.Next(1,100)).
You seemed to be working on a recursive function (dummyWhileTrue) rather than a while loop; a while loop would work too, but I kept it your way. Note that there is no break or continue in F#, so you have to be a little more structured with the if stuff inside there.
I changed your Console.WriteLine to a printfn to show off how to call it with an argument.
I showed the way to call TryParse that is most like C#. Declare your variable first (make it mutable, since TryParse will be writing to that location), and then use &n as the argument (in this context, &n is like ref n or out n in C#). Alternatively, in F# you can do like so:
let b, n = Int32.TryParse(v)
where F# lets you omit trailing-out-parameters and instead returns their value at the end of a tuple; this is just a syntactic convenience.
Console.ReadLine returns a string, which you don't care about at the end of the program, so pipe it to the ignore function to discard the value (and get rid of the warning about the unused string value).
Here's my take, just for the fun:
open System
let main() =
let answer = (new Random()).Next(1, 100)
printfn "Guess the number between 1 and 100"
let rec TryLoop(tries) =
let doneWith(t) = t
let notDoneWith(s, t) = printfn s; TryLoop(t)
match Console.ReadLine() with
| "q" -> doneWith 0
| s ->
match Int32.TryParse(s) with
| true, v when v = answer -> doneWith(tries)
| true, v when v < answer -> notDoneWith("Guess higher", tries + 1)
| true, v when v > answer -> notDoneWith("Guess lower", tries + 1)
| _ -> notDoneWith("This is not a number", tries)
match TryLoop(1) with
| 0 -> printfn "You quit, loser!"
| tries -> printfn "Correct! You win!\nYou guessed %d times" tries
printfn "Hit enter to exit"
Console.ReadLine() |> ignore
main()
Things to note:
Pattern matching is prettier, more concise, and - I believe - more idiomatic than nested ifs
Used the tuple-return-style TryParse suggested by Brian
Renamed dummyWhileTrue to TryLoop, seemed more descriptive
Created two inner functions doneWith and notDoneWith, (for purely aesthetic reasons)
I lifted the main pattern match from Evaluate in #Huusom's solution but opted for a recursive loop and accumulator instead of #Hussom's (very cool) discriminate union and application of Seq.unfold for a very compact solution.
open System
let guessLoop answer =
let rec loop tries =
let guess = Console.ReadLine()
match Int32.TryParse(guess) with
| true, v when v < answer -> printfn "Guess higher." ; loop (tries+1)
| true, v when v > answer -> printfn "Guess lower." ; loop (tries+1)
| true, v -> printfn "You won." ; tries+1
| false, _ when guess = "q" -> printfn "You quit." ; tries
| false, _ -> printfn "Not a number." ; loop tries
loop 0
let main() =
printfn "Guess a number between 1 and 100."
printfn "You guessed %i times" (guessLoop ((Random()).Next(1, 100)))
Also for the fun of if:
open System
type Result =
| Match
| Higher
| Lower
| Quit
| NaN
let Evaluate answer guess =
match Int32.TryParse(guess) with
| true, v when v < answer -> Higher
| true, v when v > answer -> Lower
| true, v -> Match
| false, _ when guess = "q" -> Quit
| false, _ -> NaN
let Ask answer =
match Evaluate answer (Console.ReadLine()) with
| Match ->
printfn "You won."
None
| Higher ->
printfn "Guess higher."
Some (Higher, answer)
| Lower ->
printfn "Guess lower."
Some (Lower, answer)
| Quit ->
printfn "You quit."
None
| NaN ->
printfn "This is not a number."
Some (NaN, answer)
let main () =
printfn "Guess a number between 1 and 100."
let guesses = Seq.unfold Ask ((Random()).Next(1, 100))
printfn "You guessed %i times" (Seq.length guesses)
let _ = main()
I use an enumeration for state and Seq.unfold over input to find the result.

Literal Attribute not working

After reading Chris' answer to F# - public literal and the blog post at http://blogs.msdn.com/b/chrsmith/archive/2008/10/03/f-zen-the-literal-attribute.aspx I don't get why the following is not working:
[<Literal>]
let one = 1
[<Literal>]
let two = 2
let trymatch x =
match x with
| one -> printfn "%A" one
| two -> printfn "%A" two
| _ -> printfn "none"
trymatch 3
This keeps printing "3", although I think it shouldn't. What is it that I don't see here?
I think that literals need to be Uppercase. The following works fine:
[<Literal>]
let One = 1
[<Literal>]
let Two = 2
let trymatch x =
match x with
| One -> printfn "%A" One
| Two -> printfn "%A" Two
| _ -> printfn "none"
trymatch 3
In addition, if you want a nice general solution for this without using literals, you can define a parameterized active pattern like this:
let (|Equals|_|) expected actual =
if actual = expected then Some() else None
And then just write
let one = 1
let two = 2
let trymatch x =
match x with
| Equals one -> printfn "%A" one
| Equals two -> printfn "%A" two
| _ -> printfn "none"
The other answers are right - you must start your identifier with an uppercase letter. See section 7.1.2 of the spec (Named Patterns), which states that:
If long-ident is a single identifier that does not begin with an uppercase character then it is always interpreted as a variable-binding pattern and represents a variable that is bound by the pattern
Also if you don't want to have Uppercase literals you can put them in a module (here named Const):
module Const =
[<Literal>]
let one = 1
[<Literal>]
let two = 2
let trymatch x =
match x with
| Const.one -> printfn "%A" Const.one
| Const.two -> printfn "%A" Const.two
| _ -> printfn "none"
trymatch 3
Don't ask me why, but it works when you write your literals uppercase:
[<Literal>]
let One = 1
[<Literal>]
let Two = 2
let trymatch (x:int) =
match x with
| One -> printfn "%A" One
| Two -> printfn "%A" Two
| _ -> printfn "none"
trymatch 3

Resources