Giter Site home page Giter Site logo

react-haskell's Introduction

React-Haskell Hackage

As crazy as it seems, using React and Haskell together just may be a good idea.

I was driven to create this thing because I had a large existing Haskell codebase I wanted to put online. However, even without existing code, I think a lot of problems are better modeled in Haskell than JavaScript or other languages. Or you might want to use some existing Haskell libraries.

Examples

Let's put a simple paragraph on the page:

sample :: ReactNode a
sample = p_ [ class_ "style" ] $ em_ "Andy Warhol"

main :: IO ()
main = do
    Just doc <- currentDocument
    let elemId :: JSString
        elemId = "inject"
    Just elem <- documentGetElementById doc elemId
    render sample elem

That creates a DOM node on the page that looks like:

<p class="style">
    <em>Andy Warhol</em>
</p>

We can make that a little more complicated with some more child nodes.

sample :: ReactNode a
sample = div_ [ class_ "beautify" ] $ do
    "The Velvet Underground"

    input_

    "Lou Reed"

But of course that input doesn't do anything. Let's change that.

sample :: JSString -> ReactNode JSString
sample = div_ $ do
    "Favorite artist:"

    input_ [ onChange (Just . value . target) ]

    text str

Getting Started

The first step is a working GHCJS installation. The easiest way is to download a virtual machine with GHCJS pre-installed. I recommend ghcjs-box.

Now that GHCJS is installed we can use cabal to create a project.

$ mkdir project
$ cd project
$ cabal init # generate a .cabal file

Now edit the cabal file to include dependencies.

build-depends:
  base >= 4.8 && < 5,
  ghcjs-base,
  ghcjs-dom,
  react-haskell >= 1.3

Now we can write Main.hs.

sample :: ReactNode a
sample = p_ [ class_ "style" ] $ em_ "Andy Warhol"

main :: IO ()
main = do
    Just elem <- elemById "id"
    render sample elem

Next Steps

Reference

Additional Resources

Is it Right for Me?

React-Haskell is a great tool for building web UI from Haskell. However, you may want to consider the alternatives:

  • By writing plain React / JSX you can speed development by avoiding the GHCJS compilation step. This also has the advantage of being a bit more universal - more people use React through JSX than React-Haskell.
  • ghcjs-react is a very similar project.
  • Reflex is an FRP system built with GHCJS in mind.

Small Print

MIT License

Bitdeli Badge

react-haskell's People

Contributors

bergey avatar bitdeli-chef avatar jeremyjh avatar joelburget 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

react-haskell's Issues

todomvc example is broken

~/react-haskell/example/todomvc$ hastec todomvc.hs

todomvc.hs:133:11:
Not in scope: type constructor or class ‘StatefulReact’

todomvc.hs:134:18:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:135:32:
Not in scope: ‘getState’
Perhaps you meant ‘getStyle’ (imported from Haste)

todomvc.hs:137:12:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:138:12:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:139:12:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:140:12:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:141:12:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:142:12:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:144:20:
Not in scope: type constructor or class ‘StatefulReact’

todomvc.hs:146:26:
Not in scope: ‘getState’
Perhaps you meant ‘getStyle’ (imported from Haste)

todomvc.hs:148:9:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:149:14:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:150:20:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:151:20:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:152:20:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:153:20:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:154:20:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:155:21:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:156:21:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:158:16:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:159:16:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:164:13:
Not in scope: type constructor or class ‘StatefulReact’

todomvc.hs:166:26:
Not in scope: ‘getState’
Perhaps you meant ‘getStyle’ (imported from Haste)

todomvc.hs:167:14:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:168:16:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:168:36:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:169:16:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:170:16:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:173:13:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:175:16:
Not in scope: type constructor or class ‘StatefulReact’

todomvc.hs:176:23:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:177:26:
Not in scope: ‘getState’
Perhaps you meant ‘getStyle’ (imported from Haste)

todomvc.hs:185:11:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:191:17:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:192:17:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:195:16:
Not in scope: type constructor or class ‘StatefulReact’

todomvc.hs:196:23:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:200:12:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:203:12:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

todomvc.hs:205:14:
Not in scope: type constructor or class ‘StatefulReact’

todomvc.hs:207:26:
Not in scope: ‘getState’
Perhaps you meant ‘getStyle’ (imported from Haste)

todomvc.hs:208:14:
Not in scope: ‘<!’
Perhaps you meant one of these:
‘!’ (imported from Haste.JSON), ‘<’ (imported from Prelude),
‘$!’ (imported from Prelude)

How to run the examples?

After installing with haste-inst install,
I run: hastec --with-js=lib/stubs.js example/simple/simple.hs --out=example/simple/main.js from the root of the project
And then open the file in chrome.
And when I type in the input box, the texts doesn't change.

Does it depend on some fixed version of ghc? I use 7.6.3

Functor instance for ReactNode

It looks as though ReactNode admits a Functor instance, which would use LocalNode but pick the insig -> sig function at the use site, rather than a type-based default. Is there a reason this instance is not provided? Would you accept a PR?

The Functor laws aren't exactly obeyed, because you can count how many times fmap has been applied. I'm not sure this matters, since the constructors aren't exported. I'd be fine with some sigMap though, with the same type but without the laws.

Failed to compile

$cabal install ./react-haskell
Resolving dependencies...
Configuring react-haskell-2.0.1...
Building react-haskell-2.0.1...
Failed to install react-haskell-2.0.1
Build log ( /home/mule/.cabal/logs/react-haskell-2.0.1.log ):
Configuring react-haskell-2.0.1...
Building react-haskell-2.0.1...
Preprocessing library react-haskell-2.0.1...
[14 of 15] Compiling React.Events ( src/React/Events.hs, dist/build/React/Events.o )

src/React/Events.hs:197:31:
No instance for (Eq JSString) arising from the literal ‘"Enter"’
In the pattern: "Enter"
In the pattern: KeyboardEvent {key = "Enter"}
In an equation for ‘handler’:
handler (KeyboardEvent {key = "Enter"}) = Just s
cabal: Error: some packages failed to install:
react-haskell-2.0.1 failed during the building phase. The exception was:
ExitFailure 1

Ghc version:
7.10.2

Cabal version:
1.22.6.0

Installed versions of packeges which react-haskell depends:

  • base 4.8.1.0
  • transformers 0.4.2.0
  • monads-tf 0.1.0.2
  • deepseq 1.4.1.1
  • lens-family 1.2.0
  • void 0.7, 0.7.1
  • aeson 0.9.0.1, 0.10.0.0
  • text 1.2.0.3
  • unordered-containers 0.2.5.1

Maybe this is the report from Cabal hell? I am trying different versions and the result is the same.

Cannot install from hackage

Using hackage version of Haste

~/react-haskell/example/todomvc$ haste-inst install react-haskell
Resolving dependencies...
Configuring Cabal-1.22.0.0...
Failed to install Cabal-1.22.0.0
Build log ( /home/maks/react-haskell/example/todomvc/.cabal-sandbox/logs/Cabal-1.22.0.0.log ):
^[[A
Installed text-1.2.0.4
Downloading hashable-1.2.3.1...
Configuring hashable-1.2.3.1...
Building hashable-1.2.3.1...
Failed to install hashable-1.2.3.1
Build log ( /home/maks/react-haskell/example/todomvc/.cabal-sandbox/logs/hashable-1.2.3.1.log ):
Configuring hashable-1.2.3.1...
Building hashable-1.2.3.1...
Preprocessing library hashable-1.2.3.1...

Data/Hashable/Class.hs:302:26:
Constructor ‘J#’ should have 1 argument, but has been given 2
In the pattern: J# size# byteArray
In an equation for ‘hashWithSalt’:
hashWithSalt salt n@(J# size# byteArray)
| n >= minInt && n <= maxInt
= hashWithSalt salt (fromInteger n :: Int)
| otherwise
= let
size = I# size#
numBytes = 4 * abs size
in
hashByteArrayWithSalt byteArray 0 numBytes salt hashWithSalt size
where
minInt = fromIntegral (minBound :: Int)
maxInt = fromIntegral (maxBound :: Int)
In the instance declaration for ‘Hashable Integer’
cabal: Error: some packages failed to install:
Cabal-1.22.0.0 failed during the configure step. The exception was:
user error ('/usr/local/bin/hastec' exited with an error:

/tmp/Cabal-1.22.0.0-8153/Cabal-1.22.0.0/Distribution/Version.hs:103:13:
No instance for (Data Version)
arising from the first field of ‘ThisVersion’ (type ‘Version’)
Possible fix:
use a standalone 'deriving instance' declaration,
so you can specify the instance context yourself
When deriving the instance for (Data VersionRange)
)
hashable-1.2.3.1 failed during the building phase. The exception was:
ExitFailure 1
nats-1 depends on hashable-1.2.3.1 which failed to install.
react-haskell-1.3.0.0 depends on hashable-1.2.3.1 which failed to install.
semigroups-0.16.0.1 depends on hashable-1.2.3.1 which failed to install.
unordered-containers-0.2.5.1 depends on hashable-1.2.3.1 which failed to
install.
void-0.7 depends on hashable-1.2.3.1 which failed to install.

Failed to install

Im just trying out ghcjs and came across these react bindings. When I install via cabal though I get the following error

[ 1 of 15] Compiling React.GHCJS      ( src/React/GHCJS.hs, dist/dist-sandbox-ce49f728/build/React/GHCJS.js_o )

src/React/GHCJS.hs:52:28:
    Module
    ‘GHCJS.DOM.Document’
    does not export
    ‘documentGetElementById’
cabal: Error: some packages failed to install:
ghcjs-dom-hello-2.0.0.0 depends on react-haskell-2.0.1 which failed to
install.

Am I missing something - I might be wrong but it looks like the function got renamed to getElementById?

Cheers

Signals delayed until first animation frame [question]

Hi @joelburget , it's me again. sorry to be a pain on here! At the moment, the signals appear to get queued when they are triggered, and they are evaluated at the next animation frame, rather than being evaluated instantly. Is there a reason for doing that? Am I likely to trigger all hell to break loose if I change it?

routing

What's our routing story? What does react-router do? What makes sense in Haskell vs JS?

finish event type definitions

What types of events are there? Currently mouse, keyboard, change, focus.

What properties do they all hold? How do properties map from js to haskell?

first class classes

It's currently impossible to render a class from a ReactT. This should be possible with locally or something.

In the best of all worlds you could do something like:

div_ $ do
    someClass
    span_ "this is not a class"

There are a few problems though...

  • someClass is not ReactT so we can't use do notation here. Okay, we could use RebindableSyntax to make this possible, but I think that's a bad idea. Instead, why not extend locally to take either a ReactT or a ReactClass.
  • The meaning of a class is not super clear. In the current formulation it's where state is stored in an IORef, but that's an implementation detail, not a means of abstraction. Maybe we don't need classes. Maybe we can figure out a clearer meaning for them. createClass also feels really weird. It's stateful in an unclear way and lives in the IO monad.

figure out packaging

I always need to call hastec example/main.hs --with-js=lib/stubs.js. Would be really nice for end users to not have to locate these stubs.

native class interop

I want to use JS-defined react classes. Once imported they should behave exactly the same as react-haskell classes.

Also see issue #8 for more on classes.

Only one element rendering at each level in the DOM

Firstly, I think react-haskell is a really good idea, and I'm using it to build a web application even if I have to keep changing all my application code and getting out of various different manifestations of cabal hell.

Secondly, I think I've found a bug:

{-# LANGUAGE OverloadedStrings #-}

import Haste.Prim
import Haste.DOM
import React

data ApplicationState = ApplicationState {
    foo :: JSString
  }

transition :: JSString -> ApplicationState -> (ApplicationState, [AnimConfig JSString ()])
transition signal oldState = (oldState, [])

index :: ApplicationState -> React ApplicationState JSString () ()
index s = do
  h1_ "Render things"
  h1_ "Render moar things"

main = do
    mElem <- elemById "app"
    case mElem of
      Nothing -> putStrLn "not found"
      Just elem -> do
          render elem =<< createClass index transition (ApplicationState "") () []
          return ()

Only renders "Render things", but I would expect "Render things" then "Render moar things". In general, the only the first child of any element in the React declaration seems to be rendering.

Fix hackage build

The hackage build is currently broken, presumably because haste-specific stuff breaks haddock. I think this can be fixed with liberal use of CPP macros.

how to run this example

https://gist.github.com/LovelyYanki/d846d32ab876853097b7

is it needed to cabal install webkit to display the react native in web widget?
if it needed to display in web, why it is called react native?
how can it replace native apps such as gtk2hs?

Prelude> :l react2.hs
[1 of 1] Compiling Main ( react2.hs, interpreted )

react2.hs:10:21: parse error on input ‘=’
Failed, modules loaded: none.

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.