4
\$\begingroup\$

Learning F#, created a somewhat elaborate fizzbuzz-builder implementation to see if I'm starting to "get" F# and thinking functionally:

type FBCase = FBCase of (int * string)

let makeFBCase num = FBCase (num, "")

let unpackFBCase case =
    match case with
    | FBCase (num, str) when str = "" -> num |> string
    | FBCase (_, str) -> str

let fbBuilder (num) (str:string) input =
    match input with
    | FBCase (x, y) when x % num = 0 -> FBCase (x, y+str)
    | _ -> input

let createFBFunc input = fbBuilder (fst input) (snd input)

let createFBPipeline inputList =
    inputList
    |> List.map createFBFunc
    |> List.reduce (>>)

let createFBPattern inputList =
    makeFBCase
    >> (inputList |> createFBPipeline)
    >> unpackFBCase

let fizzBuzz = createFBPattern [(3, "Fizz"); (5, "Buzz")]

let fizzBuzzBaz = createFBPattern [(3, "Fizz"); (5, "Buzz"); (7, "Baz")]

let fizzBuzzBazQuux = createFBPattern [(3, "Fizz"); (5, "Buzz"); (7, "Baz"); (11, "Quux")]

[1..100]
    |> List.iter (fizzBuzz >> printfn "%A")
[1..100]
    |> List.iter (fizzBuzzBaz >> printfn "%A")
[1..100]
    |> List.iter (fizzBuzzBazQuux >> printfn "%A")

Where is it obvious that I'm not thinking functionally? What could I change to make a more idiomatic F#-style program? (Note that I don't really "get" monads or workflows yet, although I'm aware they exist).

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I think it all seems quite functional. So nothing to argue about there. I like your use of List.reduce (>>).


Instead of the use of the union type FBCase, I would use the tuples directly in a way like this (some of the functions then may need a renaming):

let makeFBCase num = 
    (num, "")

let unpackFBCase (num, txt) =
    match txt with
    | "" -> num |> string
    | _ -> txt

// Instead of input I use the tuple (num, text) to make it possible to call the elements directly
let fbBuilder (denom, str) (num, txt) =
    match num % denom with
    | 0 -> (num, txt + str)
    | _ -> (num, txt)

let createFBPipeline inputList =
    inputList |> List.map fbBuilder |> List.reduce (>>)

let createFBPattern inputList =
    makeFBCase >> (inputList |> createFBPipeline) >> unpackFBCase

let fizzBuzz = createFBPattern [(3, "Fizz"); (5, "Buzz")]

let fizzBuzzBaz = createFBPattern [(3, "Fizz"); (5, "Buzz"); (7, "Baz")]

let fizzBuzzBazQuux = createFBPattern [(3, "Fizz"); (5, "Buzz"); (7, "Baz"); (11, "Quux")]

In this way I don't think you lose any clarity.


Although your efforts show a good understanding of details of functional programming, I think you make it all too complicated. The overall task is to map a list of integers to either the integer itself or a text in respect to one or more predicates. That can be done like this:

let fizzBuzz fizzBuzzes data =
    let checkFizzBuzzes value = 
        let result = fizzBuzzes |> List.fold (fun acc (num, txt) -> if value % num = 0 then sprintf "%s%s" acc txt else acc) ""
        match result with
        | "" -> value |> string
        | _ -> result

    data |> List.map checkFizzBuzzes

let data = [1..30]
data |> fizzBuzz [ (3, "Fizz"); (5, "Buzz"); (7, "Baz"); (11, "Quux") ] |> List.iter (printf "%s ")
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.