Eff monad要求使用Debug.Trace.Trace行

Fre*_*all 5 functional-programming purescript

我正在将History.js中的绑定写入PureScript,并且仍在努力理解Eff monad,一系列效果是什么以及为什么它们很有价值.现在我写了以下内容EasyFFI

type Title        = String
type Url          = String
type State        = forall a. {title :: Title, url :: Url | a}
type Data         = forall a. { | a}
type StateUpdater = Data -> Title -> Url -> Unit

-- this function is a work around for 'data' as a reserved word.
foreign import getData 
  "function getData(state){ return state['data']; }" 
  :: forall a b. { | a} -> b

unwrapState :: StateUpdater -> State -> Unit
unwrapState f s = f (getData s) s.title s.url

replaceState' :: StateUpdater
replaceState' = unsafeForeignProcedure ["data","title","url"] "History.replaceState(data,title,url)"
replaceState :: State -> Unit
replaceState = unwrapState replaceState'

foreign import data BeforeEach :: !
beforeEach :: forall e a. Eff e a ->  Eff (beforeEach :: BeforeEach | e) Unit
beforeEach = unsafeForeignProcedure ["fn",""] "window.beforeEach(fn);"
Run Code Online (Sandbox Code Playgroud)

稍后在代码中我有以下内容:

beforeEach $ do 
  replaceState {title = "wowzers!", url = "/foos"}
Run Code Online (Sandbox Code Playgroud)

并得到以下错误

 Cannot unify Prelude.Unit with Control.Monad.Eff.Eff u2518 u2517.
Run Code Online (Sandbox Code Playgroud)

我试过以各种方式操纵类型签名试图让它全部排队,但我真的不明白出了什么问题.所以它只是猜测这一点.

Phi*_*man 5

我的代码的修改版本在本文末尾,但我必须进行一些修改才能使其编译:

我假设你的意图是对StateUpdater浏览器历史产生影响,所以我改变了它的类型以使用Eff具有新History效果类型的monad .这是主要问题,因为您的最后一行使用replaceState的结果类型不在Effmonad中.这导致您看到的类型错误.

我将类型同义词中的一些通用量化类型变量移动到函数类型中.我还删除了您的Data类型同义词,并将数据内容移动到dataState类型的新字段中.

这很重要,因为你以前的Data类型没有居民.存在一种常见的误解(出于我不理解的原因),这forall a. { | a}是一种记录"我不关心字段".这是不正确的 - 这种类型表示包含可能存在的所有字段的记录类型,并且这种类型显然是无人居住的.有一个重要区别forall a. {| a} -> r,并(forall a. {| a}) -> r从查看来电者的地步.

回答你原来的问题:"什么是一排效果,为什么它们有用?" - 最初将行添加到PureScript中以处理记录类型的多态性,而无需求助于子类型.行允许我们为使用记录的函数提供多态类型,这样我们就可以在类型系统中捕获"记录的其余部分"作为概念.

在处理效果时,行也被证明是一个有用的概念.就像我们不关心记录的其余部分一样,我们通常不关心一组效果的其余部分是什么样的,只要所有效果在类型系统中正确传播.

举个例子,我修改过的代码中涉及两个效果:History和你的BeforeEach.动作beforeEachreplaceState每个动作只使用其中一种效果,但它们的返回类型是多态的.这允许两个函数的组合main具有两种效果,并且正确地键入.main具有forall eff. Eff (history :: History, beforeEach :: BeforeEach | eff) {}最常见类型的类型,由类型检查器推断.

简而言之,效果系统中的行提供了一种处理各种"原生"效果交错的简洁方法,因此开发人员不必担心诸如效果顺序或lift计算等问题mtl.


module Main where

import EasyFFI
import Control.Monad.Eff

type Title        = String
type Url          = String
type State d      = { title :: Title, url :: Url, "data" :: { | d } }

type StateUpdater d = forall eff. { | d } -> Title -> Url -> Eff (history :: History | eff) {}

foreign import data History :: !

unwrapState :: forall eff d. StateUpdater d -> State d -> Eff (history :: History | eff) {}
unwrapState f s = f s."data" s.title s.url

replaceState' :: forall d. StateUpdater d
replaceState' = unsafeForeignProcedure ["d","title","url"] "History.replaceState(d,title,url)"

replaceState :: forall eff d. State d -> Eff (history :: History | eff) {}
replaceState = unwrapState replaceState'

foreign import data BeforeEach :: !

beforeEach :: forall e a. Eff e a ->  Eff (beforeEach :: BeforeEach | e) {}
beforeEach = unsafeForeignProcedure ["fn",""] "window.beforeEach(fn);"

main = beforeEach $ do 
  replaceState { title: "wowzers!", url: "/foos", "data": {} }
Run Code Online (Sandbox Code Playgroud)