theam / haskell-do

:pencil2: :bar_chart: - The Haskell code editor focused on interactive development.
Apache License 2.0
351 stars 31 forks source link

Retroaliment the core with input from the rendering display #81

Open NickSeagull opened 7 years ago

NickSeagull commented 7 years ago

This is a very interesting feature that is awesome to have. In Jupyter, one can use the interact function to generate widgets as sliders, checkboxes, textboxes and dropdowns. These are quite cool because we can create a "Display input" typeclass, to map each input to a type:

class (Show a, Read a) => DisplayInput a where
    displayInput :: a -> _

instance DisplayInput Int where  -- Same for Float, Double, ...
    displayInput = <code to display a slider>

instance DisplayInput Bool where
    displayInput = <code to display a checkbox>

instance FromString a => DisplayInput a where
    displayInput = <code to display a text input>

instance DisplayInput MySumType where
    displayInput = dropdown [ <all the MySumType constructors (must be of kind `:: *`, which basically means that it is something like an enum)> ]

data MySumType
    = Option1
    | Option2
    | Option3 deriving (Read, Show)

How this would work?

I had an interesting talk with Simon Thompson, and this idea came out:

We setup a websocket/endpoint that is listening in some other port. We will add a script tag inside our display (when it gets generated), and that script tag has some code to send messages through the websocket/endpoint. Then these displayInputs simply are HTML elements with onclick or other attributes that just call the function defined in the script tag we created before.

In the Haskell side, the websocket/endpoint will wait until, and we, somehow, will pass a Map to our Compilation module, where, before compilation, the different variables will be substituted by the values we selected in our widgets, but we will save the previous code. The code gets compiled, rendering is executed and then we substitute the code back, so our markdown editor still has the code we've written.

Thinking about this better, it introduces some stuff that doesn't seem reasonable from a user's perspective, if they want to use the module they've generated in haskell.do in a project:

Another idea that comes to mind is use some annotation like Liquid Haskell uses, inside comments:

liquid haskell magic

Maybe we could have something like:

{-@ displayInput @-}
myValue :: Int
myValue = 10

In this way, we don't force the user to depend on the DisplayInput module, and we just define the value at the moment. In this case, it will start in 10, but as soon as the user moves the slider, it will just change the value of the variable and it will leave the value like that, which is very useful if you are fine-tuning parameters, for example, say, in a neural network.

NickSeagull commented 7 years ago

After proposing this, I think it it more flexible for us to use just normal functions/typeclasses, because, it is more extensible in the end. Maybe there could be something like a "cleanup" button?

o1lo01ol1o commented 7 years ago

I think typeclasses are the way to go, allowing users to extend the class with their own instance (potentially using one of the reactive frameworks like react flux) is way preferable to generic inputs.

(This would also make haskell-do an interactive visualization development environment: you could potentially take a haskell-do project and stick it onto an existing endpoint on some server and have a functioning data-driven webapp.)

I don't know enough about the internals of haskell-do: why wouldn't you just pass any defined display instances to GHCJS on compile and embed the bundled html/js/websocket pipe in the frontend wherever eval is called?

NickSeagull commented 7 years ago

Because that would require the user to have GHCJS installed, and definitely, that is not an option in my opinion. @o1lo01ol1o

o1lo01ol1o commented 7 years ago

Ah right, somehow I thought js was being compiled on the fly.