7
\$\begingroup\$

I'm normally a C# developer, but I've started to learn F# and I want to make sure I'm writing code in a functional way that suits the language. I've quickly pieced this together with my knowledge from reading Functional Programming for the Real World and various material from the Web.

Please look and provide feedback on style, layout or anything you want (Program.fs is where the meat is):

// DataUtils.fs
module DataUtils
open System.IO
open System.Runtime.Serialization.Json
open System.Text
let GetJsonFromObject<'T> (obj:'T) =
 use ms = new MemoryStream()
 (new DataContractJsonSerializer(typeof<'T>)).WriteObject(ms, obj)
 Encoding.Default.GetString(ms.ToArray())
let GetObjectFromJson<'T> (json:string) : 'T =
 use ms = new MemoryStream(ASCIIEncoding.Default.GetBytes(json))
 let obj = (new DataContractJsonSerializer(typeof<'T>)).ReadObject(ms)
 obj :?> 'T

// Web.fs
module Web
open System
open System.Net
open System.IO
let GetJsonFromWebRequest url =
 let req = WebRequest.Create(new Uri(url)) :?> HttpWebRequest
 req.ContentType <- "application/json"
 let res = req.GetResponse () :?> HttpWebResponse
 use stream = res.GetResponseStream()
 use sr = new StreamReader(stream)
 let data = sr.ReadToEnd()
 data

// Model.fs
module Model
open System.Runtime.Serialization
[<DataContract>]
type exchangeRate = {
 [<field: DataMember(Name = "to")>]
 toCurrency:string;
 [<field: DataMember(Name = "rate")>]
 rate:decimal;
 [<field: DataMember(Name = "from")>]
 fromCurrency:string }

// Program.fs
module Program
open System
open Model
let getExchangeRate fromCurrency toCurrency =
 let url = String.Format("http://rate-exchange.appspot.com/currency?from={0}&to={1}", fromCurrency, toCurrency)
 let json = Web.GetJsonFromWebRequest url
 let rate:exchangeRate = DataUtils.GetObjectFromJson json
 rate
let rec displayExchangeRate currencies =
 match currencies with
 | [] -> []
 | (a, b)::tl ->
 let r = getExchangeRate a b
 printfn "%s -> %s = %A" a b r.rate
 displayExchangeRate tl
let currencies = [
 ("GBP", "EUR")
 ("GBP", "USD")
 ("EUR", "GBP")
 ("EUR", "USD")
 ("USD", "GBP")
 ("USD", "EUR") ]
displayExchangeRate currencies |> ignore
printfn "Press any key to exit"
let input = Console.ReadKey()

As you can see it's not doing an awful lot at the moment, just getting some exchange rates from a REST service and displaying them. The next step would be to add exchange rates to the database. I'd normally use Entity Framework for this, but given that Functional Programming is partly about reducing the amount of state that is held, is EF even the way to go?

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Aug 4, 2014 at 15:33
\$\endgroup\$
4
  • 1
    \$\begingroup\$ DataUtils breaks parametricity. Check out fsprojects.github.io/SQLProvider for SQL data access. Generate currency combinations programmatically. Write a display exchange rate function for a single currency pair, then use a higher-order function to turn it into a function that handles many pairs. Except for the type provider, nothing about this is specific to F#, it applies to C# as well. \$\endgroup\$ Commented Aug 4, 2014 at 17:14
  • \$\begingroup\$ Thanks. I've took what you said and made some changes. This SQLProvider looks interesting, I'll also have a look at this. When you say it breaks parametricity, is that because it forces typing? \$\endgroup\$ Commented Aug 5, 2014 at 15:35
  • 1
    \$\begingroup\$ On parametricity, see bugsquash.blogspot.com/2014/05/… . It's ok to break parametricity for a one-off script, but otherwise it will hamper reasoning and compile-time checking. \$\endgroup\$ Commented Aug 5, 2014 at 17:05
  • 2
    \$\begingroup\$ @alund Just a general comment (since you're new to F#): go read every article/series on fsharpforfunandprofit.com, it is well worth the time \$\endgroup\$ Commented Aug 19, 2014 at 20:16

3 Answers 3

4
\$\begingroup\$

It looks good! Just a couple of points.

  • Instead of

    let data = sr.ReadToEnd()
    data
    

    you can write

    sr.ReadToEnd()
    
  • sprintf is more idiomatic than String.Format.

  • displayExchangeRate would be better written as a loop or using List.iter (or Seq.iter).

  • DataContractJsonSerializer.WriteObject uses Encoding.UTF8, but you're decoding with Encoding.Default, which returns the OS's current ANSI code page.

answered Aug 4, 2014 at 21:35
\$\endgroup\$
1
  • \$\begingroup\$ Thanks for the suggestions. I agree with all, so I'll make these changes. \$\endgroup\$ Commented Aug 5, 2014 at 15:36
3
\$\begingroup\$

You can destructure tuples in the function signature. So there is no need for the match in displayExchangeRate, you can just do this:

let displayExchangeRate (ccyA, ccyB) =
 let r = getExchangeRate ccyA ccyB
 printfn "%s -> %s = %A" ccyA ccyB r.rate
answered Aug 19, 2014 at 19:52
\$\endgroup\$
1
\$\begingroup\$

Your getCombinations function can perhaps be written a little more expressively as a list comprehension:

let getCombinations' list =
 [ for x in list do
 for y in list do
 if x <> y then yield (x, y) ]
answered Aug 28, 2014 at 12:35
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.