While I’m aware that my last posting could do with a fair bit of explanation for it to be really useful, I’m not in the mood to do that right now so thought I’d show something cute.
I was searching for something work related when I came across this sample in MSDN which I thought was quite cute so I decided to port it to F#. The sample plays “Mary had a Little Lamb” thought the console beeper.
Well here it is:
type tone =
| REST
| GbelowC
| A | Asharp
| B
| C | Csharp
| D | Dsharp
| E
| F | Fsharp
| G | Gsharp
type duration =
| WHOLE
| HALF
| QUARTER
| EIGHTH
| SIXTEENTH
let convert_tone t =
match t with
| REST -> 0
| GbelowC -> 196
| A -> 220 | Asharp -> 233
| B -> 247
| C -> 262 | Csharp -> 277
| D -> 294 | Dsharp -> 311
| E -> 330
| F -> 349 | Fsharp -> 370
| G -> 392 | Gsharp -> 415
let convert_duration d =
match d with
| WHOLE -> 1600
| HALF -> 800
| QUARTER -> 400
| EIGHTH -> 200
| SIXTEENTH -> 100
let mary =
[
B, QUARTER;
A, QUARTER;
GbelowC, QUARTER;
A, QUARTER;
B, QUARTER;
B, QUARTER;
B, HALF;
A, QUARTER;
A, QUARTER;
A, HALF;
B, QUARTER;
D, QUARTER;
D, HALF
]
let rec play tune =
match tune with
| head :: tail ->
begin
match head with
| REST, d -> System.Threading.Thread.Sleep(convert_duration d)
| t, d -> System.Console.Beep(convert_tone t, convert_duration d)
end;
play tail
| [] -> ()
do play mary
One thing that is slightly annoying about the sample is that the C# version is a good bit shorter. This is because C# has the concept of enums, that F# does not support. How the reason that F# does not support enums is because they are a bit dodgy, by this I mean C# will allow us to just cast any old int into an enum even if it is not a member of the enum. See what I mean:
class Program
{
protected enum Duration
{
WHOLE = 1600,
HALF = WHOLE / 2,
QUARTER = HALF / 2,
EIGHTH = QUARTER / 2,
SIXTEENTH = EIGHTH / 2,
}
static void Main(string[] args)
{
Duration d = (Duration)1232;
}
}
F# gives us its union type which is similar to enums but more powerful. However enums do one thing that union type can’t; you can directly associate an integer value with an enum, you can't do this with a union type. However after reflecting on this for a bit maybe this abstraction is a good thing. For example it would be trivial to create anther “convert_tone” function that mapped the tone type to another octave, you could then parameterise the “play” function to accept the a convert_tone function which then allows you to play your tune in any octave you whish. Well with in the limits of the consoles beep functionality!