Reputation: 16598
Unless I've made some simple error, the following pieces of code should be functionally identical:
-- This code does not compile
pg :: forall a. PG a -> Route a
pg sql = connect $ apply (runPG sql) (unsafePartial <<< fromJust <$> getConnection)
-- This code does not compile
pg sql = connect $ do
connection <- unsafePartial <<< fromJust <$> getConnection
runPG sql connection
-- This code does work
pg sql = connect $ do
connection <- getConnection
runPG sql $ unsafePartial $ fromJust connection
To help understand this, here are the relevant types:
-- Route has a MonadAff instance and all the usual stuff
newtype Route a = Route (ReaderT RouteState Aff a)
-- `connect` makes a connection to Postgres and injects it into the environment.
connect :: forall a. Route a -> Route a
getConnection :: Route (Maybe Connection)
-- PG has a MonadAff instance and all the usual stuff
newtype PG a = PG (ReaderT Connection Aff a)
runPG :: forall m a. MonadAff m => PG a -> Connection -> m a
Here is the error:
Error found:
in module AWS.Lambda.Router
at src/AWS/Lambda/Router.purs:176:70 - 176:83 (line 176, column 70 - line 176, column 83)
Could not match constrained type
Partial => t1
with type
{ client :: Client
, pool :: Pool
}
while trying to match type { client :: Client
, pool :: Pool
}
with type Partial => t1
while checking that expression getConnection
has type t0 (Maybe (Partial => t1))
in value declaration pg
where t0 is an unknown type
t1 is an unknown type
See https://github.com/purescript/documentation/blob/master/errors/ConstrainedTypeUnified.md for more information,
or to contribute content related to this error.
spago: Failed to build.
I think one of two things is going on here. Either I'm making some dumb syntax error or I don't understand Partial
as I thought I did, although it seems pretty straightforward.
Upvotes: 2
Views: 125
Reputation: 80805
This happens because type inference doesn't work very well with constraints. It doesn't always know if it needs to move constraints to the top or leave them in place. In general this is an undecidable problem, the compiler just tries to make the best effort.
Try this in the REPL:
> :t fromJust
forall a. Partial => Maybe a -> a
> :t unsafePartial
forall a. (Partial => a) -> a
> :t unsafePartial <<< fromJust
forall t2. Partial => Maybe (Partial => t2) -> t2
See what happened? The function composition has "distributed" the Partial
constraint over the parts of its argument, because it's not clear whether the constraint in fromJust
applies to the whole type or individually to a
. This might actually be a compiler bug, but it's hard to say for me right now.
So if you try to apply this function composition via <$>
to getConnection
, the compiler expects getConnection
to have this weird doubly-nested type, which it doesn't.
Interestingly, you can use unsafePartial
to remove the Partial
constraint from the whole function fromJust
, rather than just its return value:
> :t unsafePartial fromJust
forall t4. Maybe t4 -> t4
This means that you can do something like this:
pg :: forall a. PG a -> Route a
pg sql = connect $ bind (unsafePartial fromJust <$> getConnection) (runPG sql)
Upvotes: 2