1
votes

I'm totally new in ELM. Now i trying to convert some string (that i get from json) to russian translation. For example italy -> Италия.

countryConvert : String -> String
countriesTransliterationMap country =
    case country of
        italy -> "Италия"
        canada -> "Канада"

But now i receive

Any value with this shape will be handled by a previous pattern, so it should be
removed.

Whats wrong with my code?

1
You meant "italy" and "canada" (with quotes); not italy and canada (without quotes). Note you'll also need to add _ -> "default". - Mulan

1 Answers

2
votes

Your code as given does not produce the error you said it does. However, if I clean it up a bit, it will produce that error:

italy : String
italy = "italy"

canada : String
canada = "canada"

countryConvert : String -> String
countryConvert country =
    case country of
        italy -> "Италия"
        canada -> "Канада"

To be more precise, it produces that error with Elm 0.18. If you try to compile with 0.19, you'll get a slightly more informative error:

$ elm make Countries.elm 
The name `italy` is first defined here:

5| italy = "italy"
   ^^^^^
But then it is defined AGAIN over here:

17|         italy -> "Италия"
            ^^^^^
Think of a more helpful name for one of them and you should be all set!

This can still be a bit mysterious though. The key thing to understand is that case expressions have two purposes, which sometimes overlap: to compare individual values or to test structure ("shape", in the 0.18 error message's parlance) and "destructure" values -- giving local names to the parts of that structure. The Elm Guide has a nice simple example.

In order to compare individual values, you must have those values in-line in each clause of the case expression. Any variables that you use are assumed to be new declarations, local to that clause of the case expression.

So in your case expression, you aren't matching against the values that italy and canada have across the rest of your program. Instead, you're declaring two new local variables that just happen to also be named italy and canada. In other words, you haven't "taken apart" the country string at all -- all you've done is just declare a new local name for it (italy). And that's why 0.19 is complaining about shadowing. (See this explanation for why shadowing is an error rather than just a warning in 0.19.)

Since there is also no structural difference between the clauses of the case expression, 0.18 is effectively complaining that your two case clauses are identical. One will match any string an assign it to a new local variable italy; the other will also match any string, and assign it to a new local variable canada.

Alternatives that will work:

You can just inline the values:

countryConvert : String -> String
countryConvert country =
    case country of
        "italy" -> "Италия"
        "canada" -> "Канада"
        _ -> country

You can use a simple if-else construct:

countryConvert : String -> String
countryConvert country =
    if country == italy then
        "Италия"

    else if country == canada then
        "Канада"

    else
        country

Or use a Dict:

countries : Dict String String
countries =
    fromList
        [ ( "italy", "Италия" )
        , ( "canada", "Канада" )
        ]


countryConvert : String -> String
countryConvert country =
    case get country countries of
        Just c ->
            c

        Nothing ->
            country

Note that you do still need a case expression with this approach. That's because Dict.get might be passed a key that isn't in the Dict. So you might consider having your function's return type be Maybe String as well, so that it's clear that the caller might pass in a country you don't know how to translate. And that it's the caller's responsibility to decide what to do in that case.