Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you make a function take more than one type in Elm? Can you have an overloaded function?

I'm trying to port a library from JS, and I've gotten to a function that can either take a string or a list of strings. If given a string, it'll split it into a list of strings and then continue as if it had been passed that in the first place.

I can sort of do it by defining my own type, but it makes the API ugly and require a custom Type prefix your data.

Here's what I've got:

type DocumentBody = Raw String | Words List String

tokenize: DocumentBody -> List String
tokenize s = 
    case s of
        Raw str_body -> String.split " " str_body |> (List.map String.toLower)
        Words list_body -> List.map String.toLower list_body

-- Tests

tests = 
    suite "Tokenizer"
    [ test "simple" <| assertEqual ["this", "is", "a", "simple", "string"]
                                   <| tokenize (Raw "this is a simple string")

    , test "downcasing tokens: string" <| assertEqual ["foo", "bar"]
                                                      <| tokenize (Raw "FOO BAR")

    , test "downcasing tokens: list of str" <| assertEqual ["foo", "bar"]
                                                           <| tokenize (Words ["Foo", "BAR"])
    ]

Ultimately, I don't think that the port should support this kind of behavior, but how do you pattern match on just the enumerations of the type instead of needing the prefix Raw or Words in my example?

like image 811
Conrad.Dean Avatar asked Dec 21 '15 00:12

Conrad.Dean


Video Answer


1 Answers

No, you cannot overload functions in elm the same way you can in other languages. Each function has a single signature. If you want a function to accept a parameter that can be of many types, your solution using a union type works just fine.

You say it makes the API ugly. I wouldn't call it ugly. Perhaps the syntax of the type declaration or case statements is unappealing but I say, give it time. It will grow on you. There's a lot of power and safety there. You're not letting the code make any assumptions, you are forced to handle every scenario, and it's one of the strengths of working in a language like elm.

Your pattern matching code is appropriate. You cannot shorten it beyond that.

At the same time, I can understand the pain of rewriting a javascript library and trying to communicate over ports from raw javascript. You're rewriting things in a much stricter language, and you won't be able to duplicate function signatures that in javascript, accepted anything and everything. But again, that's a strength of elm, not a weakness. Use the opportunity to tighten up the API and remove ambiguity.

When it comes to your specific example, to me it feels like there are a few possible alternatives beyond your solution. I'd argue that the tokenize function is promising too much to begin with; it's too ambiguous. When writing code in functional languages, I prefer to keep things small and composable. To me, it should really be two separate functions, each with a single, specific purpose.

like image 118
Chad Gilbert Avatar answered Dec 03 '22 23:12

Chad Gilbert