I’ve been working on a Pluralsight F# course for some time. The sample project I chose is to build a web-based emulator of the classic Nokia 3310 composer application that allowed Nokia owners to program their own ringtones. I have previously written about building the tone generator and parsing the ringtone syntax with FParsec.
The final step was to wrap the composer application in a simple web page, and so
my Nokia Composer Emulator is now online!
Using FSharpX to make it easier to work with Choices
The composer module returns a
Choice<MemoryStream,string> that represents either a wave file or a string error message.
Choice is a type with two possible forms, a choice value is either a
Choice1Of2 someMemoryStream or
Choice2Of2 "error message".
To work with a choice we pattern match:
match aChoiceValue with | Choice1Of2 stream -> doSomethingWith stream | Choice2Of2 error -> printfn "Error: %s" error
I got sick of constantly pattern matching the choice, doing some calculation and repacking a choice, so I looked to FSharpx for some help. For my scenario I want do something with the successful value if there is one and pass straight through if there has been an error. FSharpX provides
Choice.map for this purpose.
Code goes from
let assembleToPackedStream (score:string) = match parse score with | Choice1Of2 tokens -> Choice1Of2 <| pack(assemble tokens) | Choice2Of2 error -> Choice2Of2 error
let assembleToPackedStream (score:string) = Choice.map (fun tokens -> pack(assemble tokens)) (parse score)
If, when processing the success value, I wanted to be able to produce an error I could have used
bind instead of
let assembleToPackedStream (score:string) = Choice.bind (fun tokens -> if thereIsAnError then Choice2Of2 "error message goes here" else Choice1Of2 (pack(assemble tokens))) (parse score)
More commonly people use the
>>= infix operator for the bind operation.
let assembleToPackedStream (score:string) = (parse score) >>= (fun tokens -> if thereIsAnError then Choice2Of2 "error message goes here" else Choice1Of2 (pack(assemble tokens)))
F# Web Application
For the web application I use the excellent asp.net mvc 5 and web api 2 F# project template.
The action I added to handle the composition post converts an error result to an exception. If the composition was created successfully then the
Content-Dispostion header is set and the wave file returned.
[<HttpPost>] member this.Produce(score:string) = match Assembler.assembleToPackedStream score with | Choice1Of2 ms -> this.Response.AppendHeader( "Content-Disposition", (ContentDisposition(FileName = "sickringtone.wav", Inline = false)).ToString()) ms.Position <- 0L this.File(ms, "audio/x-wav") | Choice2Of2 err -> failwith err
In the past I have also used the nancy F# templates which are good.