Hello
For some time I’m trying to build an abstraction that would enforce component architecture. I want to mimic a bit of Elm architecture (just a little bit) but I feel like I hit a wall with OCaml/Rescript type system.
Attempt 1
I wrote some abstractions:
module type Sandbox = {
  type model
  type msg
  type props
  let initialModel: model
  let update: (model, msg) => model
  let view: (msg => unit, model, props) => React.element
}
let useSandbox = (sandbox: module(Sandbox))  => {
  let module(Sandbox) = sandbox
  let (model, dispatch) = React.useReducer(Sandbox.update, Sandbox.initialModel)
  Sandbox.view(dispatch, model)
}
It ended up with Sandbox.props constructor trying to escape its scope.
I learned what it means by some OCaml examples but it is a bit unfortunate because it mismatches with what I wanted to do in the first place… and I just wanted to make clear, what kind of argument I expect from the function returned by Sanbox.view.
Attempt 2
So I tried this:
module type Sandbox = {
  type model
  type msg
  let initialModel: model
  let update: (model, msg) => model
  let view: (msg => unit, model, 'props) => React.element
}
let useSandbox = (sandbox: module(Sandbox))  => {
  let module(Sandbox) = sandbox
  let (model, dispatch) = React.useReducer(Sandbox.update, Sandbox.initialModel)
  Sandbox.view(dispatch, model)
}
I just remove the props type constructor and tried generic type… Unfortunately, it works as far as I won’t try to use props.
With view defined this way:
let view = (dispatch, model, props) => {
  <div>
    <h1> {text("Actual " ++ props.prefix ++ string_of_int(model.value))} </h1>
    {button(_ => dispatch(Add), "Add")}
    <button onClick={_ => dispatch(Remove)}> {text("Remove")} </button>
    <button onClick={_ => props.switchPrefix()}> {text("Switch Prefix")} </button>
  </div>
}
I’m getting this error:
Values do not match:
let view: (msg => unit, model, props) => React.element
is not included in
let view: (msg => unit, model, 'props) => React.element
Okay… I’m a bit confused here, I’m passing some concrete type but it wants… generic type?
Attempt 3
I tried a bit more, I get back to declare type props in Sandbox module, switch from simple function to functor:
module MakeSandbox = (Item: Sandbox) => {
  let useSandbox = (props) => {
    let (model, dispatch) = React.useReducer(Item.update, Item.initialModel)
    Item.view(dispatch, model, props)
  }
}
and
@react.component
let make = (~prefix: string, ~switchPrefix: unit => unit) => {
  module Sanbox = Browser.MakeSandbox(T)
  Sanbox.useSandbox({prefix, switchPrefix})
}
I finally achieved my goal (while writing this post 
). Initially, I was thinking about moving components make and makeProps to functor as well but I’m not sure if it’s even possible and I’m happy with the result so far.
But! After I failed in the first 2 attempts now my brain itches me because:
- I feel like there might solution to the first attempt, but I’m just not able to handle it
 - I don’t understand how to deal (or why I can’t deal) with a second attempt
 
Are you able to help me explain that? 