Giter Site home page Giter Site logo

ajnsit / concur Goto Github PK

View Code? Open in Web Editor NEW
304.0 25.0 21.0 4.04 MB

An unusual Web UI Framework for Haskell

Home Page: https://ajnsit.github.io/concur/

License: BSD 3-Clause "New" or "Revised" License

Haskell 10.61% Shell 0.02% JavaScript 89.37%
haskell web ui frp elm-architecture user-interface concur

concur's Introduction

Hi there πŸ‘‹

I'm also on Gitlab at https://gitlab.com/ajnsit/

  • πŸ”­ I'm currently working on strongly typed FP, user interfaces, analytics, and fintech.
  • 🌱 I love to give talks and help people learn functional programming. Get in touch!
  • πŸ‘― I'm currently looking to collaborate on products and services that make people's lives easier.
  • πŸ“« You can also reach me on twitter at the same username
  • πŸ’» I wrote the Concur UI framework, and founded the FPIndia User Group.

List of topics I'd like to talk about in future talks

  1. Writing native mobile and desktop apps using Purescript.
  2. Teaching functional programming to children.
  3. Building full stack Purescript web services using Purescript's Erlang backend.

concur's People

Contributors

ajnsit avatar demaledetti avatar pkamenarsky avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

concur's Issues

Concur brick not building

I'm getting the following build error:

   Configuring concur-brick-0.1.0.0...
    Preprocessing library concur-brick-0.1.0.0...
    [1 of 4] Compiling Concur.Brick.UI  ( src/Concur/Brick/UI.hs, .stack-work/dist/x86_64-osx/Cabal-1.24.0.0/build/Concur/Brick/UI.o )
    [2 of 4] Compiling Concur.Brick.Run ( src/Concur/Brick/Run.hs, .stack-work/dist/x86_64-osx/Cabal-1.24.0.0/build/Concur/Brick/Run.o )
    
    /Users/vilem/github/concur/concur-brick/src/Concur/Brick/Run.hs:40:35-39: error:
        Variable not in scope:
          runIO
            :: SuspendF [Image] (Free (Suspend [Image]) b) -> Maybe (IO ())

Is it still WIP?

Routing

Have you thought about a clever way to do routing with Concur? I'm planning on using something like servant-router to parse a Servant API in the client and then load the correct corresponding widget, and something like ghcjs-servant-client to generate functions to jump to the route (maybe just by doing a JS History pushState).

Then I think I'd just need a loop at the bottom that listened for changes to the location/history state and loaded the new widget / killing the old one.

But the big downside of this is that whenever I want to route a change I can't use the nice monad flow to do menus like in your example, plus it seems pretty bad to be able to jump to any other route within any widget; it could turn to spaghetti real fast.

Loading 1100 clickEl's is very slow

I have a list of 1100 users and I tried to make a button for each one (using clickEl) that returned the userId to a parent element, which I would use to jump to a new route. But it's extremely slow loading that many click elements. It could handle a couple hundred well, but approaching 1000 the performance really plummets. Once they load up, it's pretty responsive, but loading them take at least 10 seconds.

For now I just made a fake button function that uses a href instead of Concur's events machinery:

linkButton :: String -> String -> Widget HTML ()
linkButton label url =  el_ E.a [ A.href . JS.pack $ url ]
                        $ el E.button [ A.class_ "pure-button" ] [ text label ]

which is really fast and fine for now since I'm just jumping to an URL, but at some point I'm sure it would be nice/essential to be able to use clickEl's or some other event listener for a big list.

Exception handling

Is it possible(or even make sense) to implement MonadThrow/MonadCatch/MonadMask for Widget?
I'm using concur-replica.
There is some case I want to handle async exception with bracket pattern.
For example, I want to decrease the participate count for chat room when the user closes the browser which throws async exception to the coressponding concur thread.

How to build examples?

Running stack build in concur-react succeeded, but it didn't seem to have compiled the examples. The readme doesn't seem to helpβ€”what am I meant to do to get a build artefact?

I managed to get miso working and it has great examples, but concur looks promising to me, so any help would be greatly appreciated.

Use Concur VDOM using a local server

Is it possible to use Concur with a local server, compiling with GHC instead of GHCJS like Miso does with JSaddle? Can imagine it should work with the VDOM backend, but can't get it working right now. Do you have any directions?

Install instructions?

I didn't see any install instructions, but have come up with (I think) a minimal stack.yaml configuration:

resolver: lts-7.19
compiler: ghcjs-0.2.1.9007019_ghc-8.0.1
compiler-check: match-exact

setup-info:
  ghcjs:
    source:
      ghcjs-0.2.1.9007019_ghc-8.0.1:
           url: http://ghcjs.tolysz.org/ghc-8.0-2017-02-05-lts-7.19-9007019.tar.gz
           sha1: d2cfc25f9cda32a25a87d9af68891b2186ee52f9

packages:
- location: .

- location:
    git: https://github.com/ajnsit/concur
    commit: 83cf5e31d8de6f32014bb7c5becfe98683053a5e
  extra-dep: true
  subdirs:
    - concur-vdom
    - concur-core
- location:
    git: https://github.com/ghcjs/ghcjs-ffiqq
    commit: b52338c2dcd3b0707bc8aff2e171411614d4aedb
  extra-dep: true

- location:
    git: https://github.com/ajnsit/ghcjs-vdom
    commit: c9a72e5d6a2556900bfea96e3078adfc89aba858
  extra-dep: true

extra-deps:
  - natural-transformation-0.4
  - ghcjs-dom-0.9.2.0
  - ghcjs-vdom-0.2.0.0
  - ghcjs-ffiqq-0.1.0.0
  - ghcjs-dom-jsffi-0.9.2.0

flags: {}

extra-package-dbs: []

With the above, I can compile a project (and apparently concur examples) with stack setup && stack build), assuming I've already added concur-core, concur-vdom, and ghcjs-vdomtobuild-depends`.

I'm currently building after having added the dependencies for ghcjs-ffiqq, so I'll update this if needed once that's done, or ask for help if I struggle. I'm not submitting a PR because I wasn't sure where/if you wanted this. Easiest thing I can think to do is add appropriate stack.yaml files to the examples directory of each backend directory and then briefly hint at them in concur's readme.

ghc `stack build` not working for `concur-vdom`

The ghcjs version compiles perfectly, but now I'm trying to get the regular ghc version compiling so I can use it with ghci in emacs. concure-core compiles with ghc, but concur-vdom has this error:

Warning: Directory listed in concur-vdom.cabal file does not exist: ghc-src

Error: While constructing the build plan, the following exceptions were encountered:

In the dependencies for concur-vdom-0.1.0.0:
    concur-core must match -any, but the stack configuration has no specified version
    jsaddle-webkit2gtk must match -any, but the stack configuration has no specified version
                       (latest applicable is 0.9.4.0)
needed since concur-vdom-0.1.0.0 is a build target.

Recommended action: try adding the following to your extra-deps in /home/teddy/haskell/superpowerscorp/concur/concur-vdom/stack.yaml:
- jsaddle-webkit2gtk-0.9.4.0

You may also want to try the 'stack solver' command
Plan construction failed.

I got a little further by adding jsaddle-webkit2gtk as well as including the concur-core directory in stack.yaml, but then it spits out a bunch of other dependency problems.

Resuming widgets

Imagine the following scenario:

counter x = do
  _ <- div [ onClick ]
    [ text $ T.pack (show x)
    ]

  counter (x + 1)

other :: T.Text -> Widget HTML T.Text
other str = do
  e <- div []
    [ input [ onInput, value str ]
    , text str
    ]

  pure $ targetValue $ target e

container str = do
  newStr <- div [] [ counter 0, other str ]
  container newStr

I.e. a composition of both neverending and non-recursive widgets. The problem is that every time other finishes, counter is going to lose its state.

To fix this, we could "ban" recursion (and thus neverending widgets) and explicitly thread arguments between parent and children components, essentially emulating Elm, but in a somewhat free-form way. However, disallowing recursion isn't even the worst thing; to fix state loss, instead of writing a widget like this:

workflow = do
   a <- step1
   b <- step2 a
   ...
   pure b

one would have to turn the above into a state machine:

workflow = do
  st <- get
  case st of
    Step1 -> do
      a <- step1
      put (Step2 a)
    Step2 a -> do
      b <- step2 a
      put (Result b)

To me, reifying time flow is the selling proposition of Concur and something no other UI paradigm offers, to my knowledge. Going back to explicit state machines in the spirit of React or Elm doesn't make much sense.

I've thought a bit about this but the solution I've come up with feels a bit off. Basically, we'd change the type of orr to:

orr :: [Widget v a] -> Widget v (a, [Maybe (Widget v a)]) -- specialised to Widget

I.e. orr returns both the value of the ending Widget, as well as all the continuations of the remaining Widgets at that point. With this, we could rewrite the first example to:

resume = flip fromMaybe

container str c = do
  (newStr, [c, _]) <- div [] [ resume c $ counter 0, other str ]
  container newStr c

But this does not seem ideal. It would be nice if we didn't have to modify orr for this, but then there would be no way to get hold of the continuations of the non-firing Widgets. I think it should be possible to write something like this:

reify :: Widget v a -> Widget v (a, [Maybe Widget v a])

which would return the result along with all the continuations of a Widget's children, but being able to break the encapsulation of the otherwise fully opaque Widget type that easily is probably a bad idea.

I've also thought about crazy stuff like actually calling all continuations after a Widget ends, effectively running the world in parallel and introducing a join combinator - which somehow collects the results from the different "parallel universes" - but that seems like it would be awfully inefficient and probably not even possible. Sounds cool though.

Maybe I'm overlooking something fairly obvious. I saw the Gen stuff in the Purescript repo and thought about making each Widget a pipe-like thing along with yield and await operators, so that outside state can be "pushed" into neverending widgets, but this wouldn't help if widgets can still finish and thus force their siblings to lose state.

I've also had the idea of ditching the Monad constraint altogether and making Widget a selective Applicative, which still allows for some control flow but is fully introspectable. This would bring the benefit of being able to collect every UI transition upfront (and maybe even precompute DOM diffs) but more importantly, of allowing us to attach the continuations directly to the Widget VDOM node (which would never change).

However, although SelectiveDo might be implemented someday, until it isn't it's fairly cumbersome to program with selective Applicatives. So that's off the table, at least for now.

Do you have any thoughts on this?

liftIO in Widget causes strange behavior

I'm working on a nifty way to do forms, which will also hopefully be able to handle text inputs that update every key press. However, I ran into my form elements returning some really weird results, and I narrowed it down to it being caused by widgets that have liftIO in them. For example, here's a Widget I made as I was trying to figure out the problem:

testNonStateT :: Widget HTML b
testNonStateT = forever $ do
  _ <- simStateT ("one", "two", "three")
  return ()
  where
    simStateT s = do
      r <- (do
               r <- inputEnter' $ s ^. _1
               liftIO $ print "hey there!"
               return $ s & _1 .~ r)
           <|>
           (do
               r <- inputEnter' $ s ^. _2
               liftIO $ print "hey there!"
               return $ s & _2 .~ r)
           <|>
           (do
               r <- inputEnter' $ s ^. _3
               liftIO $ print "hey there!"
               return $ s & _3 .~ r )
      liftIO $ print r
      simStateT r

inputEnter' :: String -> Widget HTML String
inputEnter' t = inputEnter [ A.value . JS.pack $ t ]

When I try this, the third one will work fine, but the second one gets the third input box's return value, and the first gets the second's. However, if I uncomment the liftIO $ print "hey there!" lines, it works fine.

I'm using Concur.VDOM.

concur-sdl doesn't work

I want to experiment with native back-ends for Concur, and I wanted to use the simple concur-sdl as a basis for my experiments. However, after stack build and stack run, it crashes with this rather unhelpful message:

concur-sdl-example: thread blocked indefinitely in an STM transaction

Could you point me to what might be wrong?

PS: Thanks for this library. I used the Purescript variant for a internal web page with great success - that's why I'm now looking to use concur in native tools too (it could be great as an alternative to ImGui).

Discussion forum

Concur looks quite interesting. Do you have a place where people can discuss this with you/others?

Q: Signal or Pipe or something else?

Hello,

tl;dr When creating re-usable widgets with Concur Haskell, should I use Signals, Pipes, both, or something totally different (e.g. pulling the recursion into the parent widget)?

This is a question on the recommended way of developing reusable widgets with Concur. As a caveat, I am a complete beginner in haskell (going through Learn You a Haskell right now). I also do not intend to use Purescript at this time.

My journey started with reading the concur-documentation README, which was last updated with Signals information. I did not see any mention of pipes in there, and read through the Signal code.

When I went to look at examples, I saw that the Concur Haskell example uses Pipes. Following this, I found that Pipes were removed from the main Concur Haskell repo around the same time that Signals information was added to the documentation. On the other hand, the Concur Purescript examples use Signals. In all fairness, the concur documentation does state that examples are being provided in Purescript.

Doing a quick search, I couldn't find code for either concept in the main Concur Haskell repo, though I did see pipes in the concur-pipes repo. On the other hand, I did find Signal in FRP.purs in Concur Purescript. Maybe these concepts already exist in Haskell?

I guess the point of all this is that I just am not sure how to proceed. I also haven't been able to get concur-react-starter running yet (various compilation issues on both Windows and Linux). So I can't just copy paste the examples and try to run them either.

I think this post has been rambly enough, so I'll stop. Any pointers and guidance you can provide are greatly appreciated.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.