react-dream
react-dream copied to clipboard
Fix laws and simplify implementation
.promap was not following Profunctor laws because .map was acting on the component as-if ReactDream was the identity Functor, without any extra insights into the type. This is now fixed, but the consequence is that there are two breaking changes 💥:
.mapacts on the result of running the component, so the React element it outputs..promap’s second argument acts on the element as well.
Another effect of this is that there now no helper to apply regular higher-order components to the component inside the ReactDream. So we need a new one.
Since it is still an implementation of Functor, it could be called .map2. It’s not a fantastic name, but it’s accurate. It could probably have an alias. Quick alias brainstorm:
.hoc.wrap.update
Quick realization: it might be that HoCs need their own Fantasy Land type, since they do form a Category / Arrow.
Right. The obvious name for the method is .enhance
maybe you want to lift the component to another type where map works from Component to Component rather than from Element to Element?
a = ReactDream(props => </div>)
b = HOC(a)
b.map(hoc1).map(hoc2)
Right. 🤦♂️ 🤦♀️ this is Applicative, but on the identity functor implementation of .map
API plan update:
import ReactDream, { ReactBox } from 'react-dream'
ReactDream.Stateless(({ title }) => <h1>{title}</h1>)
.map(element => <div>{element}</div>)
.contramap(({ language }) => ({
title: language === 'en' ? 'Hello' : 'Hola'
}))
.enhance(
withState('updateTitle', 'title', 'Hola')
)
.name('Header')
ReactDream.Stateful(class extends Component {})
.map() // not optimal!
ReactDream.Stateful(class extends Component {})
.toStateless()
.map() // optimal but verbose
const withChildren = (Parent, Up, Down) =>
ReactDream.Stateless(({ parent, up, down }) =>
<Parent.Component {...parent}>
<Up {...up} />
<Down {...down} />
</Parent.Component>
)
withChildren(
Header,
Title,
Tagline
)
.contramap()
// will build a Stateful
ReactDream(class extends Component {})
// will build a Stateless
ReactDream(props => <br />)
ReactDream(props => <hr />)
.asBox()
.map()
ReactBox(props => <hr />)
.asDream()
.map()
// Equivalences
ReactDream(x).enhance(f) == ReactDream(x).asBox().map(f).asDream()
ReactBox(x).map(f).asDream() == ReactDream(x).enhance(f)
ReactBox.of(f).ap(x) == ReactBox(x).map(f)
ReactBox.of(f).ap(ReactDream(x).asBox()) == ReactBox.of(f).apDream(ReactDream(x))
Keys:
ReactBoxis introduced.ReactBoxis an oh-so-slightly sugared Identity Functor. It is there just to be transformed from and toReactDream, to provide Applicative for React Components..enhanceis just.mapof the Identity Functor. In reality,.enhancejust jumps toReactBox, maps, and then comes back.- The jump to
ReactBoxfromReactDreamis done withasBox, and the jump back is done withasDream. - In the updated React Dream, Applicative laws don’t apply. Even if they did, it is not that useful. Instead,
apDreamwill be added to the ReactBox so that lifted functions can easily be applied to ReactDreams, mimicking the old behavior of Applicative.
I’m not sure about the names .asDream and .asBox. toDream and toBox might be better ones.
Change roadmap:
- [x] Kill current
.ap - [x] Kill current
.of - [ ] Introduce Stateless and Stateful constructors with proper
inspect - [ ] Introduce
.matchon Stateless and on Stateful - [ ] Introduce
.toStatelessto Stateless and Stateful. In stateful, it just wraps with a function component..toStateless(displayName: String)takes the parameter that will set a new display name - [ ] Reimplement
.mapfor the Stateless and Stateful - [ ] Reimplement
.contramapfor the Stateless and Stateful - [ ] Reimplement
.promapfor the Stateless and Stateful - [ ] Reimplement
.concatfor the Stateless and Stateful - [ ] Reimplement
.chainfor the Stateless and Stateful - [ ] Introduce
ReactBox - [ ] Introduce
.mapto ReactBox - [ ] Introduce
.ofand.apto ReactBox - [ ] Introduce
.toDreamto ReactBox - [ ] Introduce
.toBoxto ReactDream - [ ] Introduce
.apDreamto ReactBox - [ ] Introduce
.enhanceto ReactDream
And for the next iteration:
- [ ] Create package for non-algebraic functions
- [ ] Remove non-algebraic functions