为什么 unsafePartial 不能与 PureScript 中的简单仿函数一起使用?

Why does unsafePartial not work with a simple functor in PureScript?

除非我犯了一些简单的错误,否则以下代码片段在功能上应该是相同的:

-- 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

为了帮助理解这一点,以下是相关类型:

-- 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

这是错误:

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.

我认为这里发生了两件事之一。要么我犯了一些愚蠢的语法错误,要么我没有像我想的那样理解 Partial,尽管它看起来很简单。

发生这种情况是因为类型推断不能很好地处理约束。它并不总是知道是否需要将约束移动到顶部或将它们留在原地。一般来说,这是一个无法确定的问题,编译器只是尽力而为。

在 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

看看发生了什么?函数组合对其参数的各个部分具有 "distributed" Partial 约束,因为不清楚 fromJust 中的约束是适用于整个类型还是单独适用于 a。这实际上可能是一个编译器错误,但现在对我来说很难说。

因此,如果您尝试通过 <$> 将此函数组合应用到 getConnection,编译器期望 getConnection 具有这种奇怪的双重嵌套类型,而事实并非如此。

有趣的是,您可以使用 unsafePartial 从整个函数 fromJust 中删除 Partial 约束,而不仅仅是它的 return 值:

> :t unsafePartial fromJust
forall t4. Maybe t4 -> t4    

这意味着您可以这样做:

pg :: forall a. PG a -> Route a
pg sql = connect $ bind (unsafePartial fromJust <$> getConnection) (runPG sql)