265 karma · joined June 2, 2013
1. A way to get a value that represents the type of any statically known type (i.e. known at compile time). 2. A way to compare two typereps for equality. 3. A way to convert between types if the typereps are equal.
This is basically the equivalent of the `Typeable` class in Haskell and PureScript (https://hackage.haskell.org/package/base/docs/Data-Typeable....). Both Haskell and PureScript erase types at runtime, so it is possible to do without adding a runtime to TypeScript as well.
Also you don't need to take my word for it, Brendan Eich, the creator of JS himself has said that CoffeeScript had a significant impact on his thoughts about the future of JavaScript (https://brendaneich.com/2011/05/my-jsconf-us-presentation/).
I completely understand the reasons for Rust devs targeting a C like syntax. Rust had already blown its novelty budget on the borrow checker semantics.
CoffeeScript can actually be considered very successful, since it influenced the design of a lot of other language features we take for granted. Same can be said for HAML.
"Haskell flavored Rust" anyone?
read
:: forall p
. AsRef Path p
=> p
-> Result (Vec u8)
read path = inner (as_ref path)
where
inner :: &Path -> Result (Vec u8)
inner path = do
mut file <- File.open path
let mut bytes = Vec.new
read_to_end file (&mut bytes)
pure bytesWhat I get in return is peace of mind. I’m able to stay neutral when friends, in person or online, tell me stuff. I don’t feel a need to counter with my own strong preconceived opinions. And I’ve seen that this has a tempering effect on other people too. People are less likely to be vitriolic if they don’t feel they have to make me see the error of my ways.
But what if I am letting “them” brainwash me? By not being fully “pre-informed”, am I opening myself up to misinformation that will eventually convert me into a hateful bigot? I don’t believe that is likely. First, the “them” here is friends and family. I know they come from a good place even if some of their opinions (for some of them, all of their current opinions) may be wrong at times. Being brainwashed by a horde of anonymous trolls on the internet is more likely and much worse. Second, I trust my inner compass enough to know that I’m unlikely to ever become that kind of a hateful person who actively wills harm upon a section of humanity. But I am, like most others, susceptible to being overwhelmed or confused. Opting out of the rapid bombardment of information online helps me avoid that.
Great work, and thanks for working on this and making it available!
Also not having to expose all the action datatypes when they are only being used in a single place can really improve code structure.
Currently the only thing to take care of is - when a child widget returns some value to the parent, if you want to reinvoke the child widget and somehow restore the previous local state of the child widget, you need to have sent that local state out to the parent previously. This means that you either add the local state to the child widget's return value, or use something like a wire to directly send the state to the parent or something else.
I am working on adding automatically persisted local state to Concur which would greatly simplify this. It would provide an API similar to hooks' useState (and the react bindings will actually use hooks for this).
Fortunately, you can make your own abstractions in Concur. For example, you can use a "Wire" - which lets multiple child components share and update parts of the parent's state (https://github.com/purescript-concur/purescript-concur-core/...). Note how this abstraction just uses the high level Concur API, and doesn't depend on the UI backend. With a wire the code might look something like this -
-- A wire into parent's state, accessible using `with`, and updateable using `wire.send`
counter wire = with wire \i -> do
button [onClick] [text (show i)]
wire.send (i+1)
And you can compose several counters that share state like this (the first two counters will increment together - -- Parent component creates a wire using `local` and an initial value
local 0 \w -> div []
[ counter w
, counter w
, some
, other
, widgets
]
-- Beyond this point the local state is gone and can't be accessed
doSomethingElse
2. Concur widgets can perform sync effect as well as async effects using `liftEffect` and `liftAffect`, and the results of those actions are available within the monad just like other widget results. Does that answer your question?Here's what a counter example looks like -
counter count = do
button [onClick] [text (show count)]
counter (count + 1)
Check out a comparison with elm-architecture at https://ajnsit.github.io/concur-documentation/ch03-01-replic....Great argument.
There are two cases here (1. when the target is at the front of the list and necessitates changing the head of list, and 2. when the head doesn't need to be changed.) You handle both the cases separately.
Both the classical and the "elegant" versions are worse than this one.
JUSPAY is a Payments Exchange connecting merchants, payment providers and banks. With a deep focus on delivering seamless payments UX at scale, we are trusted partners for all major internet companies in India.
We use functional programming (Haskell and Purescript) across our stack. Our Payment page is currently live with 10+ merchants. As we start to scale to 100+ merchants , we are facing some key challenges on building a unified cross-platform configurable payment page. We want to build a team of highly experienced PureScript devs to solve those problems.
Job Description - https://drive.google.com/file/d/1slLlFDc5tZ0qCtQpsCw6AHQYXqM... 1
If you find this interesting and would like to work with us, please get in touch at careers@juspay.in. Or contact me directly for any questions at anupam.jain@juspay.in.
JUSPAY provides a high throughput transaction processing system built using pure Functional Programming tech - Haskell and PureScript. We have processed over 2Bn payments so far, and currently handle 4M transactions a day. Our SDK has seen over 200M installs and is embedded inside all the top apps in India.
We are looking for Engineers who are interested in learning FP and building UI and backend frameworks to enable our large team of 200+ people.
We are practical people with deep interest in thinking from first principles and in-depth understanding of systems and scaling. FP has done a lot of magic for us. If you are interested in working with us, please drop an email to anupam.jain@juspay.in.
I find code enjoyable to read and write when it's like Lego. You build small little self contained pieces and join them together. It makes a big difference when building large applications.
I wrote an example in [Concur](https://github.com/ajnsit/concur) to demonstrate what I mean - https://gist.github.com/ajnsit/f0fee9a83480289a5a052273ce21c.... The "app" is composed of self-contained widgets, each of which are short and easy to read, and have a defined purpose (show the searchbox, render a story etc.) In a larger app, they can be mixed and matched together in logical ways.
There's an introduction/tutorial here - https://github.com/ajnsit/concur-documentation/blob/master/R...
The code snippet I posted doesn't adequately demonstrate it but the DSL is strongly typed with full type inference at every step. So for example, it knows that the type of the table after the "join" is has a field "age" of type "int", and that filtering on it is okay. Trying to filter on a non-existent field will cause a compile (not runtime) error.
Second, the "steps" are actually plain functions. `#` is simply the reverse function application operator defined as `a # f = f a`. Which means you can abstract operations. For example, you can define a generic function that can take any relation with an age field and filters for age > 60. Thanks to strong types, using it incorrectly (i.e. on a relation without an age field of type integer) would cause an compile error.
Also, the language itself is a deeply embedded DSL. So "ordinary" code also compiles down to SQL. You can do things like loops etc. (for example, create a function that filters over a list of age limits rather than just one) and it will try to generate the best SQL possible.
of course the SQL generation is currently not implemented, so all this is theoretical, but I'm getting there!
It allows writing things like -
type PersonRel = Relation (name :: String, age :: Int)
type EmployeeRel = Relation (employeeName :: String, managerName :: String)
-- Get all managers older than 60
oldManagers :: PersonRel
oldManagers =
employeeTable
# renameField (SProxy :: SProxy "managerName") (SProxy :: SProxy "name")
# join (RProxy :: RProxy (name :: String)) personTable
# filter (\r -> r.age > 60)
# project (RProxy :: RProxy (name :: String, age :: Int))The idea is that a UI "Widget" is a sequence of UIs (which themselves can be Widgets) generated asynchronously (say when a UI event fires).
A sample widget -
async function*() {
// Show a clickable button.
// All processing is async, this will conceptually block until button is clicked.
yield* <button onClick>Click me</button>
// Button was clicked, show text
yield* <div>Button was clicked!</div>
})
It uses React and VDOM to diff the UIs generated and update the page slightly more efficiently. As you can see, it also provides JSX support (by overriding createElement calls).The Widgets themselves can be easily composed, and the entire thing is designed to be extremely easy to get started with. The README on github has more information.