How to properly name F# functions (coming from OO)

I’m just learning F# for fun and trying to write some small game features in functional-style.
My case is simple:
Imagine I have record StatusEffects and I have some functions to work with it.

Example functions:
make param1 param2 OR makeStatusEffects param1 param2
apply statusEffects statusEffect OR applyStatusEffect statusEffects statusEffect
cancel statusEffects statusEffect OR cancelStatusEffect statusEffects statusEffect

The first approach is much shorter and less noisy, but second gives more information when you read it, even though it’s already clear by returned value and type hints.
What variant is more idiomatical for F#?

I don’t think either style is more idiomatic than the other. I think I probably need more domain context to give specific advice, but my initial preference is the second since it gives more information in the function name.

Some general thoughts:

  • You’re already following the camelCase convention for function names, which is good. As a note, PascalCase is still preferred for members though.

  • I personally usually side on using actionNoun but will occasionally use just action or noun. For example, if I’m writing a function for the modulus of a complex number, I’d just do let modulus z = ... instead of let calculateModulus z = .... But I have written code like let applyTransformMatrix (transformMatrix: Matrix) (tuple: ITuple<'T, _>) = ..., which is similar to your situation.

  • For functional languages like F# with automaic currying, you want to place the element that might be threaded through a pipeline as the last argument.

1 Like

In addition to the advice above…

If you have the function in a module which has the same name of the type you are working with then you can qualify the name of the function with the module name. (This is how the List, Result, Option, Array (etc.) functions are named so there’s good precedence for it.) Note: This isn’t possible in early versions of F#.

Also, using the (optional) [<RequireQualifiedAccess>] attribute forces you to give the full name which can be useful in certain circumstances.

Also, giving the parameter type names in the function name tells you what to use without relying on type hints, and is a reminder that you can’t have two (or more) functions with the same name and same parameter types.

Doing so makes it easier (for me at least) to remember where the functions are (which module), what they take as parameters, and what they apply to.

(You can forego the qualifier if you use the function within the module where it’s defined.)

See my (very simple) example script:

type StatusEffect = {
    Number : int 
    Text : string }

[<RequireQualifiedAccess>]
module StatusEffect = 

    let makeFromIntegerAndString number text = 
        { StatusEffect.Number = number ; Text = text } 

    let applyStatusEffect appliedEffect originalEffect = 
        { originalEffect with 
            Number = originalEffect.Number + appliedEffect.Number 
            Text = originalEffect.Text + " and " + appliedEffect.Text }

let start = StatusEffect.makeFromIntegerAndString 5 "five" 
let firstModifier = StatusEffect.makeFromIntegerAndString 10 "ten" 
let secondModifier = StatusEffect.makeFromIntegerAndString 20 "twenty" 

let result = 
    start 
    |> StatusEffect.applyStatusEffect firstModifier 
    |> StatusEffect.applyStatusEffect secondModifier

This may not be the ‘best’ way and other people will probably have their own ideas about this; I just find it’s comfortable for me doing it this way most of the time.

1 Like