Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is combining model and view possible? #81

Closed
stevekrouse opened this issue Nov 25, 2018 · 3 comments
Closed

Is combining model and view possible? #81

stevekrouse opened this issue Nov 25, 2018 · 3 comments

Comments

@stevekrouse
Copy link
Contributor

I know there are a lot of other issues discussing the pros and cons of separating the model and view. I'm simply wondering if it's currently possible to do given the current architecture, and if so, if someone could give me an example of it.

My motivation is that I like how in Reflex you can combine the model and the view seamlessly, but also separate them if you want. I like the extra freedom. The modelView function and the architecture it imposes feels a bit restrictive to me.

stevekrouse pushed a commit to futureofcoding/futureofcoding.org that referenced this issue Nov 26, 2018
* TOC
{: toc }

### Fun Calls

#### JE

Went really well. [Notes here.](/notes/jonathan-edwards/11-21-18) The end-result was that I should either find or build my own Reflex alternative in JS. It's easier to start from a compile-target, a DSL, than from a visual editor. Once I have this built, I can use it to think about visual abstractions to compile to it. And I can use the framework to build those abstractions in a bootstrappy way.

#### Philip Tchernavskij

PhD student in Paris also interested in the malleability of software, but coming from the HCI and political perspective. His work sounds a lot like Dynamicland.

#### Kartik Agaram

Another person interested in the same goal / themes of software malleability. It's really fun to chat with people like Kartik and Philip where we agree so much on goals but are coming from different places and see entirely different implementations of these ideas. I got Kartik, to agree to [record the conversation as an experiment](https://www.youtube.com/watch?v=DuMdDXCMDk0). I think it went well. Apparently 18 people watched it. Maybe if we continue chatting from where we left off, I can grab the audio from the conversations and turn it into a podcast. Or maybe we can do a podcast from scratch at some point.

### Blinknote

<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">💭Yesterday I wished for a dream note-taking chrome extension<br><br>🎁<a href="https://twitter.com/dankantor?ref_src=twsrc%5Etfw">@dankantor</a> supplied with me the initial code<br><br>👨‍💻I hacked for ~2 hours last night<br><br>✨I have a working Chrome extension! <a href="https://t.co/51Res4TnlI">https://t.co/51Res4TnlI</a></p>&mdash; Steve Krouse 🇬🇧 (@stevekrouse) <a href="https://twitter.com/stevekrouse/status/1065938988920422402?ref_src=twsrc%5Etfw">November 23, 2018</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

This was a really fun project, and it really energized me. In my head, hacking this together was like running up a hill and the prideful energy I felt afterwards was like running down the hill and I still feel like I'm running downhill three days later! I am actually writing this journal entry in Blinknote. It's so amazing to use tools that you make yourself. So much pride. It definitely encourages working and working with more of a smile. It also allows you to feel less boxed in - you can change things if they don't work for you. 

It makes me even more excited for my vision of programming as allowing others to make their own tools, collaboratively. Working on ones own tools makes me think of whittling a spatula from a branch with a knife. However I don't think that's the right metaphor for my system because I want to highlight the collaborative nature more.

### FoC London dinner

This was a lot more work than I expected, finding a restaurant, getting people to RSVP,  setting up payments, emailing people, dealing with dietary restrictions, confirming with restaurant, people's plans change, etc. One way to keep myself sane during this process is not to judge myself on the outcome but on the process, and even by just the fact that I'm doing it, trying things. My refrain is "full credit for showing up". I hope it goes well Wednesday!

### Kits vs apps

This is a common theme I've seen a lot ever since my visit to Dynamicland. I saw it again in Pharo and wrote up [a draft / ouline of an essay about it](https://futureofcoding.org/drafts/boundaries).

I've never liked the kit idea. In another incarnation, it's "everything is a document". I think the STEPS project had this. It's always felt messy, ugly, and lame. On one level this is just a surface thing: overlapping windows, lots of gray top navs of windows, nested menus and right-click menus. 

However on another level it feels like the messiness is a bit real. For one, it's usual a dynamicly typed system. For another, it always feels less polished and usable than apps. As in, I can't imagine my mom using it.

### I'm over the web

This is a big deal. For a while I've thought that HTML, CSS, and JS would be ok, as a compile target, but I no longer think so, particularly after Tudor showed me Pharo. He made the point that having everything in "a single render tree" is really key, and I don't feel I quite understand why but this does feel critical to me intuitively. 

Differences include: 

* single render tree, mentioned above
* different security model that will allow more things because it will expect users to understand more of the code they are "importing"
* caching that will make more sense, better offline & online story, maybe some peer-to-peer in there too
* different computational model from JS and HTML DOM

Thus my goal-system-I-have-no-name-for (wow, I really should come up with a working title for this... potluck? d1 for dream 1? I guess logichub could be d2?) will have to be built on a new system. Initially this system can be embedded within the web as a web app, but eventually I can imagine it having its own standalone "browser" that would work on various operating systems. Or I guess it could turn into an operating system itself!

Why start as a web app? I have to pick some platform and web is what I know best and I hate installing things and I love my chromebook so web seems like a solid pick.

The key question for all smalltalk-like systems: how do you prevent it from becoming its own universe? For example, Pharo or Lively Web.

My answer is that you start with single-user apps, starting with daily productivity tools, like notes, email, calendar, task management, and then once you have a critical mass, people will slowly spawn more ambitious projects that the community will use, and it will grow from there. 

I guess if it works within a webapp, people can use it on the web, so that's a pretty good story, as compared to Pharo which requires a download. So maybe the answer is that if we build it on the web to start with, it could seem like a website to most people, kinda like Lively Web or Tiddlywiki.

### Fluidity vs structure

I need to spend some time defining fluidity and structure, but this graph feels provocative:

![](https://i.imgur.com/iVXH72a.jpg)

I think fluidity has to do with live-ness, feedback loop speed, incremental actions causing incremental result, etc. And structure has to do with possible / impossible states, types, schemas. I'm stuck on where AST editors fit in this layout: they enable greater feedback loop speed knowledge of errors and prevent error states, yet they are not as fluid as using text but text doesn't give you as much info as fast.

One interesting note is that Airtable is particularly noteworth as having high structure and also high fluidity.

### Conal -> Turbine

Just as I decided (in the last meeting with JE) to build or find a JS lib for DCTP, I popped onto Twitter to find this:

<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Stumbled across another *spot-on* post about FRP by <a href="https://twitter.com/paldepind?ref_src=twsrc%5Etfw">@paldepind</a>: &quot;Let&#39;s reinvent FRP&quot; <a href="https://t.co/Ga7ywHnySI">https://t.co/Ga7ywHnySI</a> .</p>&mdash; Conal Elliott (@conal) <a href="https://twitter.com/conal/status/1066073714104532994?ref_src=twsrc%5Etfw">November 23, 2018</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

Which led me back to [Turbine](https://github.com/funkia/turbine), which I had found a few months back and mistakenly disregarded as the "wrong kind of FRP" as mistakenly interpreted their Now monad thing. I'm now quite excited about this library! I already sent two long emails to the creator, Simon, and hope he responds soon. Here's what I wrote I'd like to collaborate on:

> 1. Documentation. For example, I had to figure out how `list` worked from reading the source and puzzling together a few examples without explanations. I'd love to help document every function in the API. (Additionally, I believe the code itself could use some comment documentation but that's more up to you.)  
>  
> 2. Understanding the types of the streams I'm working with would help a lot. Maybe getting TypeScript set up ([as I failed to do in the issue above](funkia/turbine-starter#2)) would help here. 
>  
> 3. Being able to "inspect" streams better as a debugging and understanding tool. [CycleJS has this wonderful devtool](https://github.com/cyclejs/cyclejs/tree/master/devtool) and there are [a number of other really cool stream visualization tools](https://github.com/stevekrouse/futureofcoding.org/blob/95bad27a4d9e2bbd0b186b7683eecf97197fe068/drafts/frp.md#71-visualizations) we can draw on for inspiration. At the very least, a better `console.log` story would go a long way. It was really tough to figure out what was going on with my streams.  
> 
> 4. Collapsing higher-order streams is really hard, but luckily [this picture](https://user-images.githubusercontent.com/2288939/48842176-78ef8400-ed8b-11e8-8996-307d841f9ac5.png) makes it a LOT easier. It saved my life last night as I was working on my favorite FRP problem of [buttons that add buttons that add buttons... but only the odd buttons](https://codesandbox.io/s/w2kvqr5nw8). Maybe we can build on this picture somehow or at least incorporate it into the documentation...  
>  
> 5. [As stated in the issue above](funkia/turbine#81), I don't like the way the model and view are separated. I wonder if it's possible to combine them like in Reflex and other "original FRP" frameworks.

And after we/I work on these more pressing issues, the next step will be building a layer on top of that Turbine that would make the development experience *much* better. That is, building a GUI that "compiles" to Turbine, for example, kind of in the spirit of Conal's Tangible Functional Programming. Here I am referring to more radical ideas than improving the documentation or a simple devtool, such a projectional editor in the spirit of Lamdu, Luna, Isomorf, Dark, Unison, and Hazel. 

### Todos 11/26/18

* wait for Simon palepind response
  * maybe get started on some of the issues: documentation, try to combine view and model, build 7guis things, play with TodoMVC if they have it (or build it)
* reflection episode
* edit Katherine podcast
* [regroup projects](https://futureofcoding.org/log#possible-dec-2018-re-group-projects)
@paldepind
Copy link
Member

As of right now, the API in Turbine is biased towards separation of model and view. Personally, I feel that the mixing of logic and view in Reflex is messy. But, on a fundamental level, there is nothing in Turbine that prevents one from doing something similar to what is possible in Reflex. The Component monad has a Now inside it. So everything that can be done in Now can be done in Component. Furthermore, the API could easily make this more comfortable for this.

The first step would be to create a function that makes it easy to run Now computations inside a Component. Such a function could be created based on modelView as below.

function liftNow<A>(now: Now<A>): Component<A> {
  return modelView(() => now, () => emptyComponent)();
}

The above function creates a component that runs the given now computation, outputs its result, and then in the view does nothing (emptyComponent corresponds to no view). This function could also be added directly to Turbine as a primitive.

With this function, we could do something like this

const counter = go(function* () {
  const { click } = yield e.button("Click me");
  const count = yield liftNow(H.sample(H.scan((_, m) => m + 1, 0, click)));
  yield e.span(H.format`Button pressed ${count} times`);
});

Here is a CodeSandbox for the code above.

This example combines both the model and the view. The only small difference compared to Reflex is that we have to use the liftNow function whenever we want to do stateful FRP computations. If I recall correctly, in Reflex their thing corresponding to Now is not a type but a type class and this type class is implemented by their Widget type (which corresponds to our Component). I don't think having to use liftNow is a problem. Lifting in monads is very common. But, we could do the same thing as Reflex and create some type class (or the equivalent thing in TypeScript). The downside to this is that it increases the complexity of the types which makes things slightly more tricky and less transparent.

Does that help you? What do you think?

@stevekrouse
Copy link
Contributor Author

Wonderful! I'm trying to apply this to my standard "buttons that make buttons" problem, but am having trouble using it in conjunction with loop.

const buttonsThatMakeButtons = loop(({ output, count }) => function*() {
  const incrementClick = switchStream(
    output.map(l => combine(...l.map((o, i) => o.click.mapTo(i + 1))))
  );
  const count_ = yield liftNow(sample(
    scan((n, m) => n + m, 1, incrementClick.map(x => (x % 2 === 0 ? 0 : 1)))
  ));
  const output_ = yield list(
    b,
    count.map(c => Array.from(Array(c).keys()).map(x => x + 1))
  );
  return { count: count_, output: output_ };
});

Creates the error Attempt to sample non-replaced placeholder

Can you help?

https://codesandbox.io/s/w2kvqr5nw8

stevekrouse pushed a commit to futureofcoding/futureofcoding.org that referenced this issue Dec 12, 2018
* TOC
{: toc }

### Last week

Last week was productive in terms of publishing two podcast and recording a third. I also worked a bit freelance. I only need to do a dozen hours freelance at this point in conjunction with the money from the podcast sponsorship to make ends meet. Eventually with more sponsorship and Patreon, maybe I can slowly lower the number of hours freelance towards zero. 

### Github Issues / Projects

I also started my Dec Regroup Projects, including moving my todos to Github Issues / Github Projects. I now have three Github projects:

* [To Do](https://github.com/stevekrouse/futureofcoding.org/projects/3) is my pipeline of tasks
* [To Research](https://github.com/stevekrouse/futureofcoding.org/projects/2) is my pipeline of links and topics
* [My podcast pipeline](https://github.com/stevekrouse/futureofcoding.org/projects/1) is where I organize my guests

I'm a bit worried about how this new system will interact with this log. While it felt silly to copy and paste todo lists over and over in this log, it's also a bummer doing things in my new system doesn't show up in here. This log is supposed to be a log of all my work on this project, so I'd love to pull in Github issues activity somehow... Now that I say it "out loud" I wonder if I can do that automatically... For now though, I may just copy and paste some things from Github issues here when relevant.

### Jekyll refactoring

I get really fustrated with Jekyll sometimes. My main issues were:

* I was having trouble with importing page snipets 
* I wanted to be able to preview my site locally with all the CSS. (Github Pages had some setting I didn't have set.)

After many annoying hours I was able to make both of these things work. My main next big issue is a navbar on all my pages of: 

* Home
* About
* Podcast
* Log
* Community
* Contact
* Fork

Of course new styles and logo would be nice as well, and maybe a commenting system, but it's not top priority.

And to be honest, starting the Patreon is bigger priority than all of these but I'm a bit scared so I keep procrastinating...

### Turbine 7GUIs!!

After doing 3 hours of emails yesterday (I was sick towards the end of last week, so I took Thursday off and didn't do my emails Friday, sat, or Sun), I spent a few hours messing with Turbine, and then ~7 hours today in it. So fun! Despite being rough around the edges, it feels like there's really something wonderful here - I'm very impressed with the design. Polishing this is a million times better than having to start from scratch.

To take it for a spin, I've been doing the 7GUIs tasks in Turbine, and whenever I get stuck or find a bug, I write it down, and sometimes open an issue. I'm tracking all the progress in [this Github issue](#96), which I will copy and paste here:

#### Issue opened

* [typescript issue on starter kit](funkia/turbine-starter#2)
* [combining model and view](funkia/turbine#81)
* [devtools](funkia/turbine#82)
* [a single page documentation cheatsheet](funkia/turbine#84) 
* [Text input bug](funkia/turbine#86)
* [versioned documentation and various styles of `output`](funkia/turbine#85)

#### To open issue

##### Issues using `lift`:

1. Do I import it from `jabz` and do `lift(f, b1, b2)`? Or I use it as `b1.lift(f, b2)`?
2. It doesn't work in the view with error `Attempt to sample non-replaced placeholder`
3. Doesn't seem to work for 4 behaviors but the documentation says "You can also combine in this fashion any number of behaviors"

##### Constructors need to be documented across all projects

* I need the constant behaviors, streams, `IO.of` to be easier to get at than going to the tests to find them.

##### filterApply bug

`filterApply(Behavior.of(undefined).map(() => () => true), stream).log()` upon an event from `stream` errors: `predicate.at(...) is not a function`. If you replaced `undefined` with `1`, the error goes away.

##### * `time.log()` does nothing

It should at least error if it's not going to show you anything, but I might prefer it to show me all the milliseconds.

##### when doesn't log correctly

`when(Behavior.of(true)).log()` errors `Cannot read property 'Symbol(Symbol.iterator)' of undefined` but `yield sample(when(Behavior.of(true)).map(x => console.log(x)))` outputs an `OfFuture` value

#### To look into more

* What is the `moment` function (described [here](funkia/turbine#51 (comment)))?

* Use [`loop`](https://github.com/funkia/turbine#loop) successfully without `this.source.addListener is not a function` error

* What's the difference between `go` and `fgo`? Also, the placement of arguments here is confusing:

```javascript
const tempView = ({ c, f }) =>
  go(function*() {
```

* When you can do infix dot vs prefix

* I want a dummy model so I can see the view while I mess with stuff and not get `The generator function never yielded a monad and no monad was specified.` without

```javascript
const tempModel = fgo(function* ({  }) {
  yield Now.of(10)
});
```

* Need to call the function created with `performStream` or `performStreamLatest` (I don't get the difference): `performStreamLatest(book_.map(() => withEffects(() => alert('hi'))()))`

* It's also quite annoying to keep the output and inputs in sync always. 

### Priorities 12/12/18 - 12/21/18

* freelance 10 hours
* Patreon   20 hours
* Turbine   20 hours
  * finish 7GUIs
  * work on various issues

### Push to vacation or 2019

* publish Tudor and Vlad episodes
* p4 stuff
* website design and improvements
@stevekrouse stevekrouse changed the title is combining model and view possible? Is combining model and view possible? Dec 12, 2018
@paldepind
Copy link
Member

This appears to be a bug that was fixed in the latest version of Turbine.

Here is a Codesandbox that, as far as I can tell, works as expected: https://codesandbox.io/s/m9o1ok1vyy

The only things I have done is

  • Update Turbine and Hareactive to the latest version
  • Accomodate for the removal of the output property in Turbine 0.2.0
- const b = i => button({ output: { click: "click" } }, i);
+ const b = i => button(i).output({click: "click"});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants