top | item 22718405

Generate semi-dynamic UIs with Haskell

70 points| pka | 6 years ago |github.com

13 comments

order

dmitriid|6 years ago

I simply love it how every single Haskell framework I've seen that has "ease of development" and "it's easy" and similar immediately devolves from this (from Concur's docs [1]):

  hello = do
    button [onClick] [text "Say Hello"]
    text "Hello Sailor!"
into this:

  inputWidget = input [(Changed <<< unsafeTargetValue) <$> onChange, Focused <$ onFocus]
or this:

  inputWidget st = input [st {focusCount = st.focusCount+1} <$ onFocus
                         , ((\s -> st {currentText = s}) <<< unsafeTargetValue) <$> onChange]
for even the slightest modifications (which are not even complex in this case)

  These instances, allow a natural handling of 
  return values using <$>, <*>, <$, monadic 
  do-notation, etc
Right...

[1] https://github.com/ajnsit/concur-documentation/blob/master/R...

lalaithion|6 years ago

The second example can be rewritten as such:

    inputWidget = let
        changeAction = do
          v <- onChange
          return (Changed (unsafeTargetValue v))
        focusAction = do
          _ <- onFocus
          return Focused
      in input [changeAction, focusAction]
and likewise for the third:

    inputWidget st = let
        focusAction = do
          _ <- onFocus
          return (st {focusCount = st.focusCount+1})
        changeAction = do
          v <- onChange
          return (st {s = unsafeTargetValue v})
      in input [focusAction, changeAction]
(I didn't compile this, so hopefully there's no major mistakes here)

Believe it or not, many Haskell programmers prefer brevity when it comes to programs like this. They're honestly not that unreadable once you learn the meaning of a few infix operators.

chrissoundz|6 years ago

It's not clear to me what you're trying to show? Are you saying the later examples look more complex?

tome|6 years ago

I love it too, because it is a standard and flexible way to program in Haskell!

ivanbakel|6 years ago

Cool application of Haskell's purity-by-default. I'm slightly surprised this takes the approach of enumerating the whole input space to get all states, though - given that termination for big spaces isn't a priority, why not just explore all paths instead? Then you could return `Int` as much as you wanted, if the actual code only goes through finitely many different values.

pka|6 years ago

> why not just explore all paths instead?

Author here.

The problem is that concur-static generates static JS code that encodes all possible UI state transitions - so if the state space is big or infinite, so will be the resulting generated JS.

I wanted to explore the viability of generating simple static UIs with some level of dynamism. concur-static is definitely not intended as a replacement for full-blown client side UI libraries/frameworks.