reflex-platform icon indicating copy to clipboard operation
reflex-platform copied to clipboard

How to setup development environment to hack reflex and reflex-dom (without using haskell-language-server)

Open srghma opened this issue 4 years ago • 1 comments

for example I have pulled all subprojects (so that I could change their code) and entered ./try-reflex

./scripts/hack-on haskell-overlays/reflex-packages/dep/universe
./scripts/hack-on haskell-overlays/reflex-packages/dep/dependent-sum-universe-orphans
./scripts/hack-on haskell-overlays/reflex-packages/dep/reflex
./scripts/hack-on haskell-overlays/reflex-packages/dep/cabal-macosx
./scripts/hack-on haskell-overlays/reflex-packages/dep/jsaddle-dom
./scripts/hack-on haskell-overlays/reflex-packages/dep/reflex-dom
./scripts/hack-on haskell-overlays/reflex-packages/dep/ghcjs-dom
./scripts/hack-on haskell-overlays/reflex-packages/dep/dependent-monoidal-map
./scripts/hack-on haskell-overlays/reflex-packages/dep/reflex-todomvc
./scripts/hack-on haskell-overlays/reflex-packages/dep/gargoyle
./scripts/hack-on haskell-overlays/reflex-packages/dep/reflex-aeson-orphans
./scripts/hack-on haskell-overlays/reflex-packages/dep/jsaddle
./scripts/hack-on haskell-overlays/reflex-packages/dep/patch
./try-reflex

Now, I want to go https://github.com/reflex-frp/reflex-platform/blob/develop/examples/WorkOnTest/Main.hs#L4 and see the type of text (after it is instantiated)

How to do this:

  1. I see there is ghcid in the shell.nix, how to use it?
  2. OR maybe I should use holes? But when I replace main = mainWidget $ text "Hello, world!" with main = mainWidget $ (text :: _a) "Hello, world!" and run cabal build examples/WorkOnTest/Main.hs I see
Main.hs:4:24: error:
    • Couldn't match expected type ‘_a’
                  with actual type ‘text-1.2.3.1:Data.Text.Internal.Text -> m0 ()’
      ‘_a’ is a rigid type variable bound by
        an expression type signature:
          forall _a. _a
        at Main.hs:4:32-33
    • In the expression: text :: _a
      In the second argument of ‘($)’, namely
        ‘(text :: _a) "Hello, world!"’
      In the expression: (mainWidget) $ (text :: _a) "Hello, world!"

This is not the full info, I want to know WHAT IS m0?

  1. haskell-language-server and vscode could provide good experience, but it's buggy on this project

srghma avatar Aug 31 '21 11:08 srghma

  1. Whoa! What change to the ecosystem are you contemplating that involves hacking on all of those packages at the same time?! I would recommend focusing in on one thing at a time, and working your way through, you'll have a better time with rebuilds. If you're not actually hacking on any one of them, it's better not to hack-on the package, and just allow nix to get it and the things that depend on it from its cache.
  2. If I go to the documentation for reflex-dom-core and find mainWidget (which could probably use a comment along with the rest of the stuff in Reflex.Dom.Main), its type is documented as:
mainWidget :: (forall x. Widget x ()) -> JSM () 

where, clicking on Widget tells us that it's defined as:

type Widget x = ImmediateDomBuilderT DomTimeline (DomCoreWidget x)
type DomCoreWidget x = PostBuildT DomTimeline (WithJSContextSingleton x (PerformEventT DomTimeline DomHost)) 

Generally, you are not supposed to care which specific monad this is, but you will eventually end up caring that it has instances of the various classes that these monad transformers individually contribute. DomBuilder, PostBuild, and then all of the stuff provided by PerformEventT... note that despite the name, PerformEventT is no longer a monad transformer, and ends up providing a lot of basic instances.

The reason is that we do quite a lot of switching that monad out in order to provide different functionality. If you're compiling to Javascript and running the program in a web browser, that's going to look different from the monad that is used to provide a static HTML render of the initial state of the page during hydration.

Obelisk does a somewhat better job of conveying this, its Frontend record type that you fill out when defining your app looks like:

data Frontend route = Frontend
  { _frontend_head :: !(forall js t m. ObeliskWidget js t route m => RoutedT t route m ())
  , _frontend_body :: !(forall js t m. ObeliskWidget js t route m => RoutedT t route m ())
  }

It's just higher rank, so you don't get to know precisely what m is, but what's this ObeliskWidget js t route m constraint? Well, it's a comprehensive list of the things you might care will be provided when you go to write a widget:

type ObeliskWidget js t route m =
  ( DomBuilder t m
  , MonadFix m
  , MonadHold t m
  , MonadSample t (Performable m)
  , MonadReflexCreateTrigger t m
  , PostBuild t m
  , PerformEvent t m
  , TriggerEvent t m
  , HasDocument m
  , MonadRef m
  , Ref m ~ Ref IO
  , MonadRef (Performable m)
  , Ref (Performable m) ~ Ref IO
  , MonadFix (Performable m)
  , PrimMonad m
  , Prerender js t m
  , PrebuildAgnostic t route m
  , PrebuildAgnostic t route (Client m)
  , HasConfigs m
  , HasCookies m
  , MonadIO (Performable m)
  )

If that looks daunting... well, it is slightly daunting to try to think about all of it at once, but each of those is a fairly sensible bit of functionality. There's a few Obelisk-specific things there like HasConfigs and HasCookies, and PrebuildAgnostic, but the rest of it is stuff that's also being provided by mainWidget.

For example, DomBuilder is the thing that defines the primitives that let us add and control DOM nodes. You'll notice that all the widgets that put stuff in the DOM, such as the text function you were using, are constrained by it.

MonadHold is a class for monads that can create new Behaviors based on Events, by remembering (or holding on to) the previous values the Event fired with. It's the primary way that FRP programs accumulate state and remember the past.

It's probably not the most natural way forward to enumerate all this, but rather perhaps to start with some widget that you're interested in, and working backward from that to which operations are involved in its definition.

cgibbard avatar Aug 31 '21 16:08 cgibbard