fraxture
fraxture

Reputation: 5510

In Elm, how would I express verbally function type defn: `route : Url.Parser (Route -> a) a`?

I'm trying to understand this code from url-parser:

route : Url.Parser (Route -> a) a 
route =
    Url.oneOf
        [ Url.map Home top
        , Url.map BlogList (s "blog" <?> stringParam "search")
        , Url.map BlogPost (s "blog" </> int)
        ]

Is it: "route is a function that takes 1) a Parser, 2) a function that takes a Route and returns an a, and 3) an a"?

This doesn't seem correct because if it's just listing the arguments, where is it expressing the return value of route itself?

I'm very new to elm, but what throws me off here is that the -> is contained within the parens, and then there's no -> at the end indicating what route returns.

Upvotes: 1

Views: 100

Answers (2)

Gabor
Gabor

Reputation: 1676

The easy answer is that route here is not really a function, but a constant: it represents a particular kind of Parser.

Now this, of course, raises the more difficult question of what kind of parser does it represent?

And that is a rather tricky one. I believe having some background on parser combinators could probably help here (https://en.wikipedia.org/wiki/Parser_combinator).

But either way, it took me a while and I might still be getting it wrong. These are the steps I went through:

  1. Let's look at what you can do with a Parser. You can execute it using parsePath or parseHash. Each of which promising that if you provide a Parser of type Parser (a->a) a (where a is a generic type variable) then it will turn a Location into a Maybe a
  2. Let's look at what kind of parser primitives you have. string for instance is a Parser (String -> a) a, while int is Parser (Int -> a) a.
  3. Now, look at the combinators, especially </>, which is Parser a b -> Parser b c -> Parser a c. If you write string </> int, you will get a Parser (String -> Int -> c) c. This is possible, because the type variable in string can be substituted as Int -> c, so it becomes Parser (String -> Int -> c) (Int -> c).
  4. Based on no.3. I would conclude that a Parser a b represents a parser that can parse part of a complex a to reduce it to a simpler b.
  5. From a package user's perspective this goes the other way around though. If you represent your routes with the data type Route you must define a Parser (Route -> a) a to be able to turn the url into your internal representation. If one of your constructors for Route is something like MyTwoParamsRoute String Int, then you will first need to define myTwoParamsParser : Parser (String -> Int -> a) a using some of the combinators (</> or <?>) and then use map MyTwoParamsRoute myTwoParamsParse to actually apply the constructor function.

So the long answer to your original question: route : Parser (Route -> a) a represents a parser which can parse a Route out of some string (typically either the hash or the path part of the url) -- while offering the possibility that once the string is reduced by whatever Route represents, the remainder can be further parsed by a different parser.

Upvotes: 1

AntouanK
AntouanK

Reputation: 4968

Since you see just the function name before the = in route =, it means that it takes no arguments. You can also see that in the annotation since there's no ->. There is one inside parenthesis.

route therefore, returns a Url.Parser (Route -> a) a.

From what I understand, it returns a type Parser a b where a is a function (Route -> a) and b can be anything ( it's named a here, but it's irrelevant ).

The -> is referring to that function that the type Parser "encapsulates". I don't know how to explain it better, but take a look at the library functions. They work in a similar way http://package.elm-lang.org/packages/evancz/url-parser/2.0.1/UrlParser

Upvotes: 0

Related Questions