Search code examples
pattern-matchingelmalgebraic-data-types

How do you extract the value from multiple variants of a custom type?


I have a type like,

type Post 
    = Blog String String
    | Announcement String String

And a function like,

upcaseTitle : Post -> Post
upcaseTitle post =
    case post of
        Blog title body ->
            { post | title = String.toUpper title }
        Announcement title body ->
            { post | title = String.toUpper title }

I would like to write code like,

upcaseTitle : Post -> Post
upcaseTitle post =
    case post of
        Post title body ->
            { post | title = String.toUpper title }

I want to add one clause in my case statement that matches all Post types and extracts the common String, title, since it is shared across all variations of my union type.

Is this possible in elm?


Solution

  • Nope, not possible. In a few other languages this can be achieved using or-patterns (even then you still have to enumerate and deconstruct every variant, but they can share a single body), but Elm aims to be a simpler language than those.

    One option is to extract the common branch body into a function:

    upcaseTitle : Post -> Post
    upcaseTitle post =
        let
            setTitle title =
                { post | title = String.toUpper title }
        in
        case post of
            Blog title body ->
                setTitle title
    
            Announcement title body ->
                setTitle title
    

    Another is to define a separate function to extract just the title:

    getTitle : Post -> String
    getTitle post =
        case post of
            Blog title body ->
                title
    
            Announcement title body ->
                title
    
    upcaseTitle : Post -> Post
    upcaseTitle post =
        { post | title = String.toUpper (getTitle post) }
    

    But if every variant of your custom type is identical, I'd start by questioning the design of that type. Maybe it would be better to use a record where the post type is just a field instead?

    type PostType
        = Blog
        | Announcement
    
    type Post =
        { title : String
        , body : String
        , postType : PostType
        }