Replacing function parameter with its alias breaks compilation

So, I have wasted good 5 hours of today just trying to figure out the type of the routeHandler function in the code below. I almost succeed, but my attempts to replace the parameter with an alias result in type mismatch and I don’t understand why.

Given this code working code:

module Main where

import Prelude hiding ((/))

import Effect.Aff (Aff)
import HTTPurple

data Route = Home
derive instance Generic Route _

route :: RouteDuplex' Route
route = mkRoute { "Home": noArgs }

routeHandler :: ∀ a. { route :: Route | a} -> Aff Response
routeHandler { route: Home } = ok "home"

main :: ServerM
main = serve { port: 8080 } { route, router: routeHandler }

I replace the type of routeHandler arg with its complete alias as:

module Main where

import Prelude hiding ((/))

import Effect.Aff (Aff)
import HTTPurple

data Route = Home
derive instance Generic Route _

route :: RouteDuplex' Route
route = mkRoute { "Home": noArgs }

-- We couldn't figure what this type is, let's temporarily name it somehow
type RouteHandlerArg = ∀ a. { route :: Route | a }
routeHandler :: RouteHandlerArg -> Aff Response
routeHandler { route: Home } = ok "home"

main :: ServerM
main = serve { port: 8080 } { route, router: routeHandler }

and I get this error:

in module Main
at src/Main.purs:20:46 - 20:58 (line 20, column 46 - line 20, column 58)

  Could not match type

    ( body :: { buffer :: Ref (Maybe Buffer)
              , stream :: Stream
                            ( read :: Read
                            )
              , string :: Ref (Maybe String)
              }
    , headers :: RequestHeaders
    , httpVersion :: Version
    , method :: Method
    , path :: Array String
    , query :: Object String
    , url :: String
    )

  with type

    a1

What? Why??

These two definitions are not the same:

routeHandler :: ∀ a. { route :: Route | a} -> Aff Response
type RouteHandlerArg = ∀ a. { route :: Route | a }
routeHandler :: RouteHandlerArg -> Aff Response

In the second case, your forall is scoped differently. If you were to expand the alias you would actually have:

routeHandler :: (forall a. { route :: Route | a) -> Aff Response

If you want to put your { route :: Route | a } type in an alias you would need to pass a as an argument, instead of capturing it inside the alias:

type RouteHandlerArg a = { route :: Route | a }
routeHandler :: ∀ a. RouteHandlerArg a -> Aff Response
1 Like

I see, now it is much clearer, thank you!