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 requireds
? - 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.
source to share
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
).
source to share