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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With