mikesol / purescript-deku

A PureScript web UI framework
https://purescript-deku.surge.sh/
Apache License 2.0
123 stars 12 forks source link

Complicated type specialization repros #44

Closed xvaldetaro closed 1 year ago

xvaldetaro commented 1 year ago

Following up on the discussion on Discord. Got complicated cases that I got stuck with today. Tried to isolate the repro as much as I could, but I don' understand the types very well to go any further: case 1: I can't use Nut in topLevelNutEv and need to specify everything. This case is a house of cards for me. Anything I change I get stuck

module Main where

import Prelude

import Control.Alt ((<|>))
import Deku.Control (switcher, text_)
import Deku.Core (Domable, Nut)
import Deku.DOM as D
import Deku.Interpret (FFIDOMSnapshot)
import Deku.Toplevel (runInBody)
import Effect (Effect)
import FRP.Event (AnEvent, Event, bang, bus, fromEvent, keepLatest)

main :: Effect Unit
main = do
  runInBody switcherNut

  where
  switcherNut :: ∀ l. Domable Effect l (FFIDOMSnapshot -> Effect Unit)
  switcherNut = switcher identity (fromEvent (topLevelNutEv <|> bang loadingNut))

  topLevelNutEv :: ∀ l. Event (Domable Effect l (FFIDOMSnapshot -> Effect Unit))
  topLevelNutEv = topLevelNut <$> envEv

  envEv :: Event (Env Effect)
  envEv = keepLatest (myIntEv <#> createWithMyInt)
    where
    createWithMyInt myInt = bus \push event -> {myInt, appPush: push, appEvent: event}

  myIntEv :: Event Int
  myIntEv = fromAff $ pure 42

  topLevelNut :: ∀ l. Env Effect -> Domable Effect l (FFIDOMSnapshot -> Effect Unit)
  topLevelNut _ = D.div_ [ text_ "Top Level"]

  loadingNut :: Nut
  loadingNut = text_ "Loading..."

fromAff :: Aff ~> Event
fromAff a = makeEvent \k -> launchAff_ (a >>= liftEffect <<< k) *> pure (pure unit)

data AppEvent

type Env m =
  { myInt :: Int

  -- App Bus
  , appEvent :: AnEvent m AppEvent
  , appPush :: AppEvent -> m Unit
  }

Case 2: Just trying to create a function that returns AnEvent, but needed to specify all of this:

module App.Route where

import Prelude hiding ((/))

import Control.Monad.ST.Class (class MonadST)
import Data.Generic.Rep (class Generic)
import Data.Maybe (Maybe(..))
import Data.Monoid.Always (class Always)
import Data.Monoid.Endo (Endo)
import Data.Show.Generic (genericShow)
import Effect (Effect)
import Effect.Class.Console (log)
import FRP.Event (AnEvent, fromEvent, makeEvent)
import Routing.Duplex (RouteDuplex', parse, root, segment)
import Routing.Duplex.Generic (noArgs, sum)
import Routing.Duplex.Generic.Syntax ((/))
import Routing.Hash (matchesWith)

routeChangeEvent :: ∀ s m. Always (m Unit) (Effect Unit)
  => Always (Endo Function (Effect (Effect Unit))) (Endo Function (m (m Unit)))
  => Monad m
  => MonadST s m
  => AnEvent m Route
routeChangeEvent = fromEvent $ makeEvent \k -> do
  matchesWith (parse routeCodec) \old new ->
    when (old /= Just new) do
      log $ "Changing route from " <> show old <> " to " <> show new
      k new

-- Stuff below is just for the file to compile
data Route
  = Landing
  | Room String
  -- Debug Learning Routes
  | PlaygroundDummy
  | PlaygroundFrp
  | PlayerList
  | CreatePlayer

derive instance genericRoute :: Generic Route _
derive instance eqRoute :: Eq Route
derive instance ordRoute :: Ord Route
instance showRoute :: Show Route where
  show = genericShow

routeCodec :: RouteDuplex' Route
routeCodec = root $ sum
  { "Landing": noArgs
  , "Room": "roomId" / segment
  -- Debug Learning Routes
  , "PlaygroundDummy": "playgrounddummy" / noArgs
  , "PlaygroundFrp": "playgroundfrp" / noArgs
  , "PlayerList": "playerlist" / noArgs
  , "CreatePlayer": "createplayer" / noArgs
  }

Side question: I created a type Nut_ s m l p = Korok s m => Domable m l p so that I can create functions where the types have AnEvent and Nut so that we can match m. I.e. : nutFromRoute :: forall s m l p. AnEvent m Route -> Nut_ s m l p Am I doing the right thing, or is there a simpler way to do this?

xvaldetaro commented 1 year ago

This another weird one:

module Nuts.TopLevel where

import Prelude

import App.Env (Nut_)
import Bolson.Core as Bolson
import Data.Monoid.Always (class Always)
import Deku.Control (text)
import Deku.Core (class Korok)
import Deku.DOM as D
import Effect (Effect)
import FRP.Event (AnEvent, bang, makeEvent)

usingEffect
  :: forall s m lock logic obj a
   . Korok s m
  => Always (m Unit) (Effect Unit)
  => m a
  -> (a -> Bolson.Entity logic obj m lock)
  -> Bolson.Entity logic obj m lock
usingEffect effect f =
  Bolson.EventfulElement' (Bolson.EventfulElement (fromEffect (f <$> effect)))

fromEffect :: ∀ a m. Monad m => m a -> AnEvent m a
fromEffect effect = makeEvent \k -> do
  emission <- effect
  k emission
  pure $ pure unit

nut :: ∀ s m l p. Nut_ s m l p
nut = usingEffect (pure $ bang 42) $
  -- works
  \myIntEv -> closureWithInt myIntEv
  -- doesn't work
  -- closureWithInt

  where
  closureWithInt :: AnEvent m Int -> Nut_ s m l p
  closureWithInt myIntEv =
    D.div_ [text $ show <$> myIntEv]
mikesol commented 1 year ago

Don't know how I missed this - sorry about that & I'll look at this today!

mikesol commented 1 year ago

Looking over this, for now I'll punt until @PureFunctor has finished up his work using Mermaid. I think that'll simplify signatures immensely.

mikesol commented 1 year ago

Closing as all of these signatures are simplified now 🚀