4
\$\begingroup\$

I have created a simple calculator that will assist in creating magic items from the tabletop RPG Pathfinder. Here are the full requirements, and if you want a more concise version, here is a player generated summary of the process.

I have been trying to get into F# and functional programming in general. I come from a strong object-oriented background and it has been difficult in ignoring those habits. Please focus on whether or not this is idiomatic F#. I would love to hear if there is a better way to write any of it. And, if you find bug, I'd be happy to know that too.

Questions

A couple of things specifically that I feel could be better would be the DC calculation. I wasn't sure how to approach applying the rush function only if the item was rushed. I also tried finding a place where I could apply functional composition, but I think I needed all the intermediate steps that I stop on.

Code

let baseDC casterLevel =
    5 + casterLevel

let rush dc =
    dc + 5

let ignoreRequirements requirements dc =
    dc + (5 * requirements)

let getBuildPrice basePrice =
    basePrice * 0.5

let getTime basePrice =
    floor (8.0 * basePrice / 1000.0)

let getMinBonusForNoCursed dc =
    dc - 6

let buildDaysWhileAdventuring isRushed buildTime =
    let adventuringBuildTime = buildTime / 4.0
    floor (if isRushed then adventuringBuildTime else adventuringBuildTime * 2.0)

[<EntryPoint>]
let main argv = 
    printfn "%A" argv

    let isRushed = true
    let casterLevel = 1
    let requirementsIgnored = 1

    let basePrice = 5000.0
    let buildPrice = getBuildPrice basePrice

    let buildTime = getTime basePrice
    let daysAdventuring = buildDaysWhileAdventuring isRushed buildTime

    let DC = 
        match isRushed with
        | false -> baseDC casterLevel |> ignoreRequirements requirementsIgnored
        | true -> baseDC casterLevel |> ignoreRequirements requirementsIgnored |> rush

    let minDC = getMinBonusForNoCursed DC

    printfn "Min/DC: %i/%i Cost: %.2f Days/Hours: %.1f/%.1f" minDC DC
        buildPrice daysAdventuring buildTime

    0 // Return code
\$\endgroup\$
2
\$\begingroup\$

I see a few problems with this, most notably, you have a plethora of magic numbers.

let baseDC casterLevel =
    5 + casterLevel

let rush dc =
    dc + 5

let ignoreRequirements requirements dc =
    dc + (5 * requirements)

let getBuildPrice basePrice =
    basePrice * 0.5

let getTime basePrice =
    floor (8.0 * basePrice / 1000.0)

let getMinBonusForNoCursed dc =
    dc - 6

let buildDaysWhileAdventuring isRushed buildTime =
    let adventuringBuildTime = buildTime / 4.0
    floor (if isRushed then adventuringBuildTime else adventuringBuildTime * 2.0)

In order, I see:

  1. 5 + casterLevel: what is 5?
  2. dc + 5: what is 5? Is it the same as #1?
  3. 5 * requirements: what is 5? Is it the same as #1 or #2?
  4. basePrice * 0.5: what is 0.5? Is it 1 / 2 or something different?
  5. 8.0 * basePrice: what is 8.0?
  6. #5 / 1000.0: what is 1000.0?
  7. dc - 6: what is 6?
  8. buildTime / 4.0: what is 4.0?
  9. adventuringBuildTime * 2.0: what is 2.0, is it 4.0 / 2, is it the 4.0 from #8 / 2?

For a functional program, it's not too bad. I don't like how many times you use if (I always prefer match), but when dealing with boolean types it does quite fine.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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