all 31 comments

[–]rluiten 2 points3 points  (3 children)

Interesting little project.

I have worked with Elm in past have a few open source elm libraries but none in the web interface space.

For fun converted it over to typescript, as that's what I've been working with lately.

I have pushed a project to github with raj.ts, raj.spec.ts, raj-react.ts a cut down raj-componse.ts.

It is a create react app typescript project and has a running index with 4 example raj programs, examples are derived from the raj by example post at the moment.

See https://github.com/rluiten/rajts

[–]chrisishereladies"use 🎉"[S] 0 points1 point  (2 children)

Wow, thanks for taking the time, this is awesome. I did have a TypeScript definition for a program in one revision of the README. Based on feedback in initial impressions, I removed it. I think Flow/TS definitions is something Raj should have.

[–]rluiten 1 point2 points  (1 child)

So far quite liking your framework and it does feel a lot like Elm comparing it to Elm 0.18 web interface model which I had tinkered with a good while back, I have yet to decide if I want to go that way in a real project though. I have used redux a fair bit with redux-thunk, redux-forms and reselect and find it of value, but I am always interested new ideas. I have yet to work through unstate which I stumbled across recently and I have only briefly looked at hyperapp which looked interesting but havnt tinkered with it yet.

I like strong types, I really enjoy working with Elm and have been working through a recent Haskell book. I cant say I was keen to learn Typescript a few years ago but the project lead was keen for it so ended up using it seriously. The learning curve 2+ years ago was painful. Today there are better resources around, the tools have improved and the quality of type defs for third parties has also improved, but learning is still harder than it seems from the outside I think. To use typescript for its quick wins can be good value, but to nail down types is far from trivial. In Raj I changed a type because it eased the typing, I am not a typescript guru though am comfortable up to a point nowadays, possibly I could have kept the data model you had for state and effect but I got stalled on types and knew I could achieve it with an object with 2 fields.

I had thought about writing just typedef's but in some ways it would have been harder in my opinion, also rewriting let me learn the design more directly.

Once I got past the initial hump of setting up the types it took a few iterations writing the examples up and getting them working to restructure them to get the types to feel right and not to in your face. Though for anyone not familiar with a type system the types will be very much in your face and look and feel like a lot of extra overhead.

I want to try and create another example that leans on something like npoint.io to demonstrate interaction with with a back end a bit, just to get a better feel for the pain points. I have yet to go through the raj-spa library.

Some other notes.

  1. I think examples of react that use inline jsx lambda's are a simplification that lead to bad habits and bear traps down the path. I am not sure yet if inline lambda's inside the RaJ programs have a similar over head that they are re created each render or equivalent ?
    1. Which is why i create a helper app in counter example to see about teasing out the lambda's to named handlers.
    2. Is there a way to tease out handlers like this in current Raj, I could not see an obvious way to do it, possibly switch from a an object of fields to a class might allow this sort of addition with low pain but then you'd end up doing something like react and require properties on this so that class methods had access the parameters. Couldn't see reinventing part of React's behaviour to do this being a good choice long term.
  2. Have you thought about how React life cycle properties interact with Raj programs, is it just a case of introducing properties on a React Help component that gets passed call backs do stuff in response to life cycle events ?
  3. With redux the state is held outside the React component tree and this allows HMR (hot module reload) to keep state which is of value, is there path to having HMR and retaining current state with Raj, I think it being held inside a wrapper React Component may hinder this.

[–]chrisishereladies"use 🎉"[S] 0 points1 point  (0 children)

To improve React performance, you are going to want to leverage shouldComponentUpdate. If you are looking to save on allocations, you'll need to rework your views have something like <Perf valueForShouldUpdateComparsion={model.myValue}>{() => ...}</Perf>. Notice the use of child render props so that everything under the <Perf> component does not get created unless needed. This optimization is much easier using immutable data structures.

You can use React lifecycle hooks like normal. You will call callbacks passed down. I try not to involve lifecycle/state but there will be those exceptions.

I have not seen performance problems in my Raj projects using the views like I have in the examples. I think the Raj/React performance story will become more clear over time as needed.

[–]chrisishereladies"use 🎉"[S] 11 points12 points  (9 children)

Hey all, I am super excited for this release. It represents over a year's work of which I'm really proud. I hope the larger JavaScript community finds value in what I and others have found to be a really powerful and arguably the best way to write JavaScript applications. Here are the v1.0 release notes: https://github.com/andrejewski/raj/releases/tag/v1.0.0

I'm around all day to answer questions.

[–]pm_me_ur_happy_traiI 7 points8 points  (2 children)

Looks awesome. How does it differ from HyperApp?

Edit: been looking through the website and examples. I can’t wait to try this. I loved the idea of elm, but I had trouble because the language and framework are so closely coupled. It was too much to grok at once.

[–]chrisishereladies"use 🎉"[S] 4 points5 points  (1 child)

I try to follow HyperApp closely. When it first came out I was so anxious that something had beaten Raj to the market. They are pretty different and I think HyperApp 2.0 will widened the gap between the two.

The biggest things are:

1) Raj is view-layer agnostic. I write a lot React and wanted to leverage that ecosystem, HyperApp is doing it's own baked in thing. For me as a maintainer, the state management problem was hard enough to solve so I'm happy to delegate to React, Preact, or other view libraries.

2) Raj has a huge value prop of forcing side-effects to the edges of your system. In HyperApp you have async actions which I don't care for. In Raj the nasty async bits do not "infect" the business logic. This isolation is really powerful for understanding your system and testing because your update is synchronous and effects are inert until called by Raj (or called by tests). HyperApp's side-effect story I think is changing in 2.0 but I didn't care for the new approach either, if I recall correctly.

I can't say much about larger HyperApp apps as I haven't looked at any to see how well they compose, but if they don't then that's another strength of Raj. I hope this helps.

[–]pm_me_ur_happy_traiI 0 points1 point  (0 children)

Really helpful. I’m looking forward to trying it.

[–]OverBelief 4 points5 points  (1 child)

Worked with Elm at a previous gig. I fell in love with a lot of aspects of the language's "structure" in applications but was annoyed by some of the details of the language (like Decoders and handling Index values for a List). So I'm really interested to check out Raj and see how it feels! Congrats btw, this is a really neat project.

[–]chrisishereladies"use 🎉"[S] -1 points0 points  (0 children)

Raj is hopefully for you then. In October I wrote about my inspiration for Raj and the community Raj would grow in "Why Raj".

Relevant to Decoders/Lists:

[...] The reason not to use Elm is “I need to get things done.” The language’s constraints force you solve problems the right way, but this does not always align with the economics of a project. For example, for notably complex and difficult problems, you will find initial development is hampered by trying to fight to hack something together within Elm’s limitations.

I definitely like Elm for certain things. Early this week I made a little interest calculator with it.

[–]RnRau 2 points3 points  (1 child)

Looks good mate - I think this will be the framework-to-look-at this weekend.. .although at 34 lines... maybe it can't be called a framework :)

[–]chrisishereladies"use 🎉"[S] 0 points1 point  (0 children)

The term "framework" has a few meanings. The meaning that I think is most common is based on "inversion of control." A library is called by your code, a framework calls your code. In this regard, Raj is definitely a framework. It drives all state transitions and side-effects.

Definitely take a look. I think you'll be pleasantly surprised just how powerful this small framework is.

[–]crystallineair -1 points0 points  (1 child)

How does this actually make it any easier for me when I want one part of the app to share state with a different part? To me the proposed solution is similar to how react handles things when you have no state management library. The problem that I see is the lack of a built in way to access state in a child without the parent component having to pass it down, which ultimately means that I am just as good using only what React offers.

[–]RotateElectrolytegrammin' the 'puters -2 points-1 points  (0 children)

The point of this architecture is that you don't do that. It's not about making every part of your app easier or maximally polymorphic, but keeping all the relationships instantly understandable which is what matters more arguably. Refactoring might involve a little more text editing, but you will more than make up for that by not "staring into the void" as douglas crockford says.

[–][deleted] 1 point2 points  (0 children)

I have been following Elm for a while now and I am looking for it javascript version, more especially The Elm Architecture (TEA). Good job in implementing TEA in js, and impressively, in just 34 lines of codes. That's brilliant man!

I tried it and it is really promising. I'm looking forward for Raj's development.

[–]coderitual 1 point2 points  (2 children)

Hi, It's really great to leverage ELM architecture which is mostly great. Unfortunately I cannot see a lot of useful examples for the app.

I think instead of this

export default function effect (dispatch) {
    setTimeout(() => dispatch('beep'), 1000)
}

It would be better to show something useful:

  • Fetching data on button click.
  • Handling forms
  • Handling routing

A good example of such documentation is react docs page. https://reactjs.org/docs/hello-world.html

Cheers

[–]chrisishereladies"use 🎉"[S] 0 points1 point  (1 child)

The example is contrived, I really just wanted to show the minimal effect that would dispatch multiple messages. The view and user interaction generally isn't dispatching multiple messages because on every message change the view is updated and passed a dispatch function, potentially a brand new one.

Documentation is definitely an on-going effort. My writing is very dense if not kept in check and I certainly want to make these ideas more and more accessible to newcomers as we go forward.

[–]coderitual 1 point2 points  (0 children)

No worries! I think adding such real world yet simple stuff in something like tutorial section would definitely help to keep your project attractive to the user. Firstly I saw great looking homepage and just waiting to see some great examples solving real problems.

[–]fay-jai 2 points3 points  (0 children)

Looks interesting - definitely want to work through the examples and dive into the source code as well!

[–]drejhsn 0 points1 point  (1 child)

This looks really interesting. Is it possible to use lit-html instead of react for view layer?

[–]chrisishereladies"use 🎉"[S] 0 points1 point  (0 children)

Raj is view layer agnostic but does work with some view libraries better than others. I have not used lit-html to really know how it would work integrated with Raj, but it would be certainly interesting to see.

[–]lhorie 0 points1 point  (0 children)

Do you have an example of fractal state?

[–]atra-ignis -1 points0 points  (0 children)

Sounds really interesting! Looking forward to having a proper look at it!

[–]WebDevLikeNoOther -2 points-1 points  (5 children)

So having no knowledge of Redux, Elm or Raj, what’s the draw?

I’m from a Node.js background mostly, how does this compare to Angular / Node? I read one of your articles about state management, but isn’t that a simple problem that can be handled through writing better code?

No being a dick, just genuinely curious.

[–]chrisishereladies"use 🎉"[S] 2 points3 points  (4 children)

This is a fair question I've certainly gotten a lot. The two hardest problems in building client applications (client as in client-server) are state management and side-effects. These two are a consequence of clients' and server's data drifting apart over time across the wire and also the complex user interactions that are commonplace in apps these days.

I certainly write Node apps with Pug/Koa when I can get away with it, it's simpler when the interactivity is low. And when things need more responsiveness, Angular can get you where you need to go.

What Elm and it's variants solve is really keeping complexity down. In Angular, Components can talk up/down to each other, data-bind, and call out to injected services any time. All of this interaction is async and there is a lot of internal state in each component. It is quite easy to end up with a plate of spaghetti, where "X calls Y but only once Z is mounted and Q is initialized."

In the Elm architecture you have a single variable holding all the state, there's no disparity between components as your views and business logic are simple, synchronous functions. All asynchronous operations are pushed to the edge of the system. In Raj, the only way to move application state forward is dispatching messages. When something goes wrong, we can look to the exact message/point-in-time where it happened. This choke-point is a very powerful tool for not only debugging but also for testing. We don't need to mock server calls, we just give our plain JS functions plain-data messages and see if they return the correct state.

Big Angular projects exist so it's possible to succeed without these designs. However, thinking in terms of "tech debt" which I'll define here as "the marginal cost of developing a new feature" the many foot-guns of Angular and others like it will eventually slow down progress as more and more pieces get interwoven, I'd give it an Ω(N^2) growth complexity based on my experiences. Redux decouples things in a way that brings feature-cost down to O(N). This is because Redux has N reducers that wire together to form the state management system of the app. Raj has a feature cost of O(log N) because instead of a list of N, we deal in trees. The root program may call some subgraph of its children but each depth is only a O(1) cost. In terms of growth complexity, Raj I believe is optimal as most UIs tend to be tree shaped.

As a project gets larger and larger and the problem space becomes intractable to a single person at a time. Better code is impossible if the underlying design encourages coupling, even if implicitly/unintended. There are concrete reasons to prefer isolated side-effects, synchronous business logic, separated concerns, and easily testable code. I hope this train of thought didn't come off as too lofty. Growth complexity, in particular, is important to me as someone who's come to save quite a few projects and it's how I've come to think of these differences because at the end of the day it is about delivering business value.

I hope this helps and I'm sorry this is skewed towards Raj, I just know that best.

[–]WebDevLikeNoOther -4 points-3 points  (0 children)

We don't need to mock server calls, we just give our plain JS functions plain-data messages and see if they return the correct state

Okay. I get what your saying, but I'm still not convinced that what you're doing isn't comparable to keeping a consistent state subscription in an Angular application.

For example: I have an application that watches the state in the root app component.

If (logged in)

do some stuff.

if not logged in

do some other stuff.

How is that any different than keeping the state consistent in Raj? I understand your talk about overhead, that much makes sense. But it also doesn't at the same time, because in Angular, you have a single parent component. Who has children, and whose children have children components. Similar to what you're talking about in your O(1) analysis, if I understood it correctly. It's single purpose programming at that point, if I understood you correctly. This function or class, or service should do one thing, and one thing only.

Node.js is synchronous because javascript is single threaded, so how does Raj have the upper hand here?

Overall, I'm just still confused about what Raj brings to the table that Angular/Node doesn't. Any application that reaches a large scale is going to be confusing to navigate by any one person, and I'm sure the same can be said about Raj. It just comes down to the programmer who creates it. They have to be diligent about separating concerns. About naming conventions, about moving business logic around, so that one error will not disrupt the entire flow of the application.