【问题标题】:Effects and phantom types效果和幻象类型
【发布时间】:2015-07-11 16:33:08
【问题描述】:

假设我们有三个对象:

MainObj {
  someProp: false
  toggleSomeProp: function () {
    if (this.someProp)
      this.someProp = false
    else
      this.someProp = true
  }
  ...
}

FirstObj {
  someOtherProp: ...
  doSomethingWithOtherProp: function () {...}
  ...
}

SecondObj {
  state: null
  setState: function (s) {
    this.state = s
  }
  getState: function() {
    return this.state
  }
  ...
}

FirstObjSecondObjMainObj 继承 someProptoggleSomeProp 并使用自己的属性和方法对其进行扩展。 SecondObj 扩展了 MainObj 的 state 属性(和 get/set 方法),可以是任何东西。

假设我们有两个对象FirstObjSrcSecondObjSrc,它们都有getObj 方法。第一个返回FirstObj,第二个返回SecondObj

这就是我在 Purescript 中看到的实现方式:

foreign import data ObjEff :: * -> !
foreign import data Obj :: *
foreign import data FirstObjSrc :: *
foreign import data SecondObjSrc :: *

foreign import somePropImpl :: forall a s e. a -> Eff (oe :: ObjEff s | e) Boolean
foreign import toggleSomePropImpl :: forall a s e. a -> Eff (oe :: ObjEff s | e) Unit

foreign import someOtherPropImpl :: ...
foreign import doSomethingWithOtherPropImpl :: ...

foreign import getStateImpl :: forall a b s e. (a -> Maybe a) -> Maybe a -> b -> Eff (oe :: ObjEff s | e) (Maybe s)
foreign import setStateImpl :: forall a s e. a -> s -> Eff (oe :: ObjEff s | e) Unit


foreign import getFirstObjImpl :: forall a s e. FirstObjSrc -> Eff (oe :: ObjEff s | e) a
foreign import getSecondObjImpl :: forall a s e. SecondObjSrc -> Eff (oe :: ObjEff s | e) a


class MainObj a where
  someProp :: forall s e. a -> Eff (oe :: ObjEff s | e) Boolean
  toggleSomeProp :: forall s e. a -> Eff (oe :: ObjEff s | e) Unit

class FirstObj a where
  someOtherProp :: ...
  doSomethingWithOtherProp :: ...

class (MainObj a) <= SecondObj a where
  getState :: forall s e. a -> Eff (oe :: ObjEff s | e) (Maybe s)
  setState :: forall s e. a -> s -> Eff (oe :: ObjEff s | e) Unit

class ObjSrc a where
  getObj :: forall b s e. a -> Eff (oe :: ObjEff s | e) b


instance objIsMainObj :: MainObj Obj where
  someProp = somePropImpl
  toggleSomeProp = toggleSomePropImpl

instance objIsFirstObj :: FirstObj Obj where
  someOtherProp = someOtherPropImpl
  doSomethingWithOtherProp = doSomethingWithOtherPropImpl

instance objIsSecondObj :: SecondObj Obj where
  getState = getStateImpl Just Nothing
  setState = setStateImpl

instance firstObjSrcIsObjSrc :: ObjSrc FirstObjSrc where
  getObj = getFirstObjImpl

instance secondObjSrcIsObjSrc :: ObjSrc SecondObjSrc where
  getObj = getSecondObjImpl

foreign import getFirstObjSrc :: forall s e. Eff (oe :: ObjEff s | e) FirstObjSrc
foreign import getSecondObjSrc :: forall s e. Eff (oe :: ObjEff s | e) SecondObjSrc

所以,我对这段代码有一些疑问:

  1. 这个实现是否正确?
  2. ObjEff 效果是否需要幻像类型s
  3. 如果有(或没有),那么我想了解原因(我已经阅读了https://wiki.haskell.org/Phantom_type 和其他一些的解释,我认为我了解基础知识,但效果让我感到困惑小)。

更新

假设上面的代码是某种虚构的浏览器(或 NodeJS)API,所以无法以某种方式更改它。

【问题讨论】:

  • 函数式编程的基石之一是拥有不可变的数据结构并将任何可变状态保持在最低限度。不要有可变数据结构,让您的切换函数将旧数据结构作为参数,并返回包含新值的新数据结构。
  • @Bzzt 这个问题不是关于toggleSomeProp,而是关于statetoggleSomeProp 只是一个可以用... 代替的噪音。上面的代码是浏览器原生代码的简化版本,所以实际上我无法让我的切换函数将旧数据结构作为参数。 EventCustomEvent 可能是很好的例子。
  • 我会检查像 purescript-simple-dom 这样的库,看看他们是如何做到的。对于不同的方法,purescript-halogen 使用 virtual-dom。
  • @Bzzt Virtual-dom 是关于渲染,而不是关于事件监听。而purescript-simple-dom 不支持自定义事件。实际上,由于removeEventListener 的实现方式,我对这个库有点怀疑。

标签: functional-programming purescript


【解决方案1】:

此问题基于该语言的较旧且不兼容的版本,而不是当前版本。 Eff 和效果行已被删除,取而代之的是Effect(本质上是Eff 无效果行)。我猜* 已被Type 取代,! 已被删除(当我开始使用 PureScript 时,这些符号并不存在)。

用于对象的表示法有点令人困惑,因为它既不是 JavaScript 也不是我熟悉的任何其他标准表示法。我的解释是,例如,

MainObj {
  someProp: false
  toggleSomeProp: function () {
    if (this.someProp)
      this.someProp = false
    else
      this.someProp = true
  }
  ...
}

表示这个(在 JavaScript 中)

function MainObj() {
}
MainObj.prototype.someProp = false;
MainObj.prototype.toggleSomeProp = function () {
  if (this.someProp)
    this.someProp = false
  else
    this.someProp = true
}
// ...

在这种情况下,PureScript 中对此的可能定义是:

foreign import data MainObj ∷ Type

foreign import someProp ∷ MainObj → Effect Boolean

foreign import toggleSomeProp ∷ MainObj → Effect Unit

实现文件是:

exports.someProp = function (mainObj) {
  return function () {
    return mainObj.someProp;
  };
};

exports.toggleSomeProp = function (mainObj) {
  return function () {
    return mainObj.toggleSomeProp();
  };
}

或者为了更好的内联和更容易的实现,这个:

foreign import data MainObj ∷ Type

foreign import someProp ∷ EffectFn1 MainObj Boolean

foreign import toggleSomeProp ∷ EffectFn1 MainObj Unit

而实现将是:

exports.someProp = function (mainObj) {
  return mainObj.someProp;
};

exports.toggleSomeProp = function (mainObj) {
  return mainObj.toggleSomeProp();
}

有多种方法可以完成(并且已经完成),但这是我几乎唯一使用的方法,因为它直截了当、显而易见且适应性强。

someProp 必须作为有效导入,因为给定相同的对象它可以返回不同的结果。 toggleSomeProp 必须以有效的方式导入,因为它以可观察的方式(通过 someProp)改变状态。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-11
    • 1970-01-01
    • 2013-08-14
    • 1970-01-01
    • 1970-01-01
    • 2010-09-16
    • 2012-07-02
    相关资源
    最近更新 更多