Functional programming Eff monad要求具有Debug.Trace.Trace的行

Functional programming Eff monad要求具有Debug.Trace.Trace的行,functional-programming,purescript,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 StateUpdate

我正在将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);"
在后面的代码中,我有以下内容:

beforeEach $ do 
  replaceState {title = "wowzers!", url = "/foos"}
并得到以下错误

 Cannot unify Prelude.Unit with Control.Monad.Eff.Eff u2518 u2517.

我尝试过以各种方式操纵类型签名,试图让它们对齐,但我真的不明白出了什么问题。所以这只是猜测

我对您代码的修改版本在本文末尾,但为了使其可编译,我不得不做一些修改:

我假设您的
StateUpdater
的目的是对浏览器历史记录产生影响,因此我将其类型更改为使用带有新的
history
影响类型的
Eff
monad。这是主要问题,因为您的最后一行使用了
replaceState
,其结果类型不在
Eff
monad中。这导致了您看到的类型错误

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

这很重要,因为您以前的
数据
类型没有居民。有一种常见的误解(出于我不理解的原因)认为所有a。{a}
是一种“我不关心字段”的记录类型。这是不正确的-此类型表示包含可能存在的所有字段的记录类型,并且这种类型显然是无人居住的。所有a的
之间有一个重要的区别。{a}->r
(对于所有a.{a})->r
从调用方的角度来看

在回答您最初的问题:“什么是一行效果,它们为什么有用?”-行最初添加到PureScript中,以处理记录类型上的多态性,而不必求助于子类型。行允许我们为使用记录的函数提供多态类型,这样我们就可以在类型系统中捕获“记录的其余部分”作为一个概念

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

举个例子,我修改的代码中涉及两种效果:
History
,以及您的
beforeach
。每个操作之前的
replaceState
都只使用其中一种效果,但它们的返回类型是多态的。这使得
main
中两个功能的组合既有效果,又能正确键入
main
具有所有eff的类型
。Eff(history::history,beforeach::beforeach | Eff){}
这是类型检查器推断出的最通用的类型

简言之,效果系统中的行提供了一种处理各种“本机”效果交错的简洁方法,因此开发人员不必担心效果顺序或提升等问题



菲尔,你真是个神。
输入别名x=…
我不知道你能把
x
放在那里,这对我来说很好。如果可以,我想请你喝杯啤酒。这种纯脚本语言震撼了我的整个生活。
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": {} }