Effects and phantom types

Let's say we have three objects:

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

      

FirstObj

and SecondObj

inherit from someProp

and toggleSomeProp

from MainObj

and extend it with your own properties and methods. SecondObj

extends MainObj

with a state property (and get / set methods), which can be anything.

Let's also say that we have two objects FirstObjSrc

and SecondObjSrc

that have a method getObj

. The first returns FirstObj

and the second returns SecondObj

.

This is how I see it implemented in 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

      

So, I have some questions about this code:

  • Is this implementation correct?
  • Is the ObjEff

    phantom type effect required s

    ?
  • If this is the case (or not), then I would like to understand why (I read the explanation at https://wiki.haskell.org/Phantom_type and some others and I think I understand the basics, but the effects confuse me a little) ...

Update

Let's say the above code is a kind of imaginary browser (or NodeJS) API, so there is no way to change it in any way.

+3


source to share


1 answer


This question is based on an older and incompatible version of the language than the current one. Lines Eff

and Effect

have been removed, and in its place - Effect

(essentially Eff

no effect lines). I assume it *

was replaced with Type

and !

removed (these entries did not exist when I started using PureScript).

The notation used for objects is a little confusing because it isn't JavaScript or any other standard notation I'm familiar with. My interpretation is, for example, that

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

      

Means this (in JavaScript)

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

      

In this case, a possible definition for this in PureScript is:

foreign import data MainObj ∷ Type

foreign import someProp ∷ MainObj → Effect Boolean

foreign import toggleSomeProp ∷ MainObj → Effect Unit

      

And the implementation file will be:



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

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

      

Or for better embedding and easier implementation, this is:

foreign import data MainObj ∷ Type

foreign import someProp ∷ EffectFn1 MainObj Boolean

foreign import toggleSomeProp ∷ EffectFn1 MainObj Unit

      

And the implementation will be:

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

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

      

There are different ways that you can (and do) this, but I use this method almost exclusively because it is simple, obvious, and easily adaptable.

someProp

should be imported as efficient, since when the same object is present, it may return different results. toggleSomeProp

should be imported as efficient because it changes state in a visible way (via someProp

).

0


source







All Articles