[SOLVED] Halogen: how to run main without making a component?

I’m just starting out and I’ve read different tutorials on using Halogen. What confuses me is that every example I see starts main by making components, however “Real World Halogen” says, quoting:

Reach For Components Last

Halogen developers don’t reach for components right away. Components are useful to manage statefulness and communication with the rest of the application in addition to rendering HTML. If you just need to render some HTML or it is simple to pass in state and events, then just use a small function. […]

Alright. So I do the following (imports omitted):

main :: Effect Unit
main = HA.runHalogenAff do
  runUI myhtml unit $ HA.awaitBody

myhtml =
  HH.header [] [ HH.text "Hello world" ]

…and I get an error Could not match type 'HTML' with type 'Component t0 t1'. How do I run HTML then, is there an alternative runUI?

Hey there! Looks like that’s a snippet from my guide.

You can’t ‘run’ HTML. You need a component at the root of your application. A good basic example is in the start of the Halogen guide:
https://purescript-halogen.github.io/purescript-halogen/guide/index.html

After you have a root component, then the advice you linked applies.

1 Like

Thank you, I see. What was very confusing is that initialState of mkComponent is mandatory, but since I don’t have any “initial state” (as I’m just rendering HTML and have “initial states” hardcoded), I’d have to pass some dummy value. This observation goes well in line with your blog post mentioning not to use components unless you have to. So it would seem like mkComponent is not the way to go, and there’s some other way.

But I guess it’s just an oddity in Halogen’s design, that’s all…

For posterity, the “hello world” that works for me to just render a header:

module Main where

import Prelude

import Effect (Effect)
import Halogen as H
import Halogen.Aff as HA
import Halogen.HTML as HH
import Halogen.VDom.Driver (runUI)

main :: Effect Unit
main = HA.runHalogenAff do
  body <- HA.awaitBody
  runUI component unit body

component :: ∀ query input output m. H.Component query input output m
component =
  H.mkComponent
    { initialState: const 0
    , render
    , eval: H.mkEval H.defaultEval
    }
  where
    render _ = HH.header_ [ HH.text "Hello World" ]

Yeah, Halogen requires a component at the root — after all, if all you do is render HTML without any state then there isn’t much reason to use Halogen!

What you’ve done is about as simple as you can get. You’ll usually see Unit as the state type instead of an integer because unit generally denotes “nothing to see here”

1 Like