Pattern for default values

Hi, I’m curious what’s your preferred way to provide default values to functions.

One example from JS world

let defaultOptions = {
  optionA: 'A',
  optionB: 'B',
  optionC: 'C'
};

function myFn(options) {
  options = Object.assing({}, defaultOptions, options);
  ...
}

myFn({ optionB: 'D'});

https://ellie-app.com/5ZbNh2XFBdfa1

In Elm, I’m forced to state all the options when calling myFn, otherwise it won’t compile. Is there any nice patter how to provide override for default options?

Thank you

You can use the record update syntax for that, so your code in the Ellie would look like

{ defaultOptions | optionB = "D" }

2 Likes

My favorite is the Optionals -> Optionals pattern, which I learned from @dillonkearns

Here’s an example for a text input

type alias Required msg = 
    { onInput : String -> msg
    , value : String
    , placeholder : String
    }

type alias Optionals msg =
    { attrs : List (Attribute msg)
    , onEnter : Maybe msg
    , leftDecoration : Html msg
    , rightDecoration : Html msg
    , id : String
    }


defaults : Optionals msg
defaults =
    { attrs = []
    , onEnter = Nothing
    , leftDecoration = none
    , rightDecoration = none
    , id = ""
    }

textInput: Required msg -> (Optionals msg -> Optionals msg) -> Html msg
textInput required optionals =
    let
        opts =
            optionals defaults
    in...

You then call the function like this:

textInput
    { onInput = Input, value = model.string, placeholder = "Type something" }
    (\d -> { d | onEnter = Just Submit, attrs = [ class "stuff" ] })

And if you don’t want to set any optional arguments at all, you use the identity function:

textInput
    { onInput = Input, value = model.string, placeholder = "Type something" }
    identity
2 Likes

You can watch this talk:

which essentially shows various ways to solve this problem. Here are the basics:


--- many functions

defaultFoo : Required -> Foo

barredFoo : Bar -> Required -> Foo

barredAndBazzedFoo : Bar -> Baz -> Required -> Foo

customFoo : { bar : Bar, baz : Baz, qux: Int, required : Required } -> Foo

--- default options

type alias Options =
     {  bar : Bar
     , baz : Baz
     , qux: Int
     }

defaultFooOptions : Options 
defaultFooOptions =
     { bar = defaultBar
     , baz = defaultBaz
     , qux = 0
     }

foo : Options -> Required -> Foo

--call with
foo { defaultFooOptions | qux = 3 } required

--- Options list

type Option
    = Bar Bar
    | Baz Baz
    | Qux Int

qux : Int -> Option -- etc

foo : List Option -> Required -> Foo

-- call with
foo [ qux 4 ] required
 
-- with pattern

type Builder 

init : Required -> Builder

withBar : Bar -> Builder -> Builder
withBaz : Baz -> Builder -> Builder
withQux : Int -> Builder -> Builder

toFoo : Builder -> Foo

-- call with
init required 
    |> withQux 4
    |> toFoo

Watch the talk so see some of the pros/cons of these approaches.

7 Likes

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.