You are viewing archived messages.
Go here to search the history.

Paul Tarvydas 2024-11-04 02:24:28

For discussion...The face of hardware is vastly different than it was in the 1950s and as it was in the early days of computer-ing, when concepts like programming languages and operating systems were invented. I think that this means re-imagining how we use what we've got, instead of just tweaking what we've got. It augments the meaning of "programming". To start things off, here are some thoughts: open.substack.com/pub/programmingsimplicity/p/the-future-of-programming?r=1egdky&utm_campaign=post&utm_medium=web&showWelcomeOnShare=true

📝 The Future of Programming

2024-11-02

Kartik Agaram 2024-11-04 04:19:46

Routing must be performed in an atomic manner to avoid possible interleaving of messages from other places.

Containers must process each message to completion before inhaling another message.

I think this might answer my long-standing question about timing. So if a component has two input ports, there's no automatic way to consume from both in parallel?

Paul Tarvydas 2024-11-05 21:08:59

I think this might answer my long-standing question about timing. So if a component has two input ports, there's no automatic way to consume from both in parallel?

Correct.

I believe that this kind of thing is an Architectural decision and should not be built in under-the-hood into the tools. Components can have multiple input ports. The tool only guarantees that the inputs are queued up in order of arrival. The Architect decides what to do about the inputs.

Further... multi-processing is essentially a fiction in FP. ...

Further... there is exactly one "race condition" in reality. ...

programmingsimplicity.substack.com/p/multiple-input-ports-only-one-input?r=1egdky

📝 Multiple Input Ports, Only One Input Queue

Software Components, Race Conditions 2024-11-05

Guyren Howe 2024-11-07 00:46:49

I am quite interested in Event Handlers.

In college, I remember in my languages course discussing how you could have static scope or dynamic scope — whether a non-local identifier is searched for in the scope where the thing being called was defined, or in the scope where it’s being called.

Static won out, because in general it’s easier to reason about.

I mention all this, because I think I understand what Event Handlers are and how they work, and afaict, they’re just “a thing with dynamic scope”.

That all seems fine and good, but I never hear EH being described that way. Maybe because folks have forgotten about dynamic scope?

Joshua Horowitz 2024-11-07 08:48:47

I think you’re talking about (algebraic) effect handlers, not event handlers. Otherwise, I’m not sure what “event handler” refers to.

Duncan Cragg 2024-11-07 14:35:08

yeah I'm struggling with this, but it sounds interesting... do go on...

Guyren Howe 2024-11-07 16:50:42

Yes: Algebraic Effect Handlers. My bad.

Joshua Horowitz 2024-11-07 19:58:48

(FWIW, based on my limited understanding, I agree that effect handlers are dynamically scoped. Some related things in the modern world that use dynamic scoping: dependency injection, Scala’s ‘implicits’.)

Guyren Howe 2024-11-07 19:59:35

Nice observation.

J. Ryan Stinnett 2024-11-08 16:09:29

Many algebraic effect systems are indeed dynamically scoped, but there are also others (like Effekt, effekt-lang.org) that use lexical effect handlers (regaining the simpler reasoning you usually have with lexical / static scope).

J. Ryan Stinnett 2024-11-08 16:25:20

This paper (from the Effekt lead) compares dynamic vs. lexical effect handlers, along with other aspects. Might be good to read: link.springer.com/chapter/10.1007/978-3-030-83128-8_3 (It should be open access, if you can't access it, let me know.)

Joshua Horowitz 2024-11-08 21:36:22

J. Ryan Stinnett Thanks for the pointer! Section 4.1 in that paper is really clarifying to me. I think some of my understanding of this static/dynamic scope stuff has been off.

I’ve thought of Scala implicits as dynamically scoped, because implicitly passed parameters to a function are passed on to things called in the function. But this sense of “things called in the function” is actually lexically defined, not dynamically defined! That is, if you define a closure in your function’s body, you pass implicit parameters to functions called in the closure – this binding can’t be intercepted by functions that end up on the call stack between the two functions.

This is perfectly analogous to the situation described in Section 4.1. So there’s some mechanism working here that I’ve always thought of as “dynamic scope” even though it’s really just “lexical scope implicitly propagated through function calls”. I think real dynamic scope is something you couldn’t resolve at compilation time, but all of these things (Effekt’s handlers, Scala implicits) are statically resolvable.

Hmm!

(Looks like Effekt was previously implemented as a Scala library: cambridge.org/core/services/aop-cambridge-core/content/view/A19680B18FB74[…]_type_and_effectsafe_extensible_effect_handlers_in_scala.pdf)

🕰️ 2024-10-21 08:13:19

...

Mariano Guerra 2024-11-08 12:19:10

The deadlines for the workshop proposal approaches, should we coordinate somewhere else? who wants to help?

An alternative name I thought about is "Computing Systems Collective" 😄

Jared Forsyth 2024-11-09 13:22:23

I've put together some thoughts about syntax, they're pretty roughly defined at the moment, but I'd love to get your feedback! gist.github.com/jaredly/593d66a955b09572f3810b43b75a22a1

Misha A 2024-11-09 14:56:16

The structured editor that I'm building

for which language(s)?

Jared Forsyth 2024-11-09 15:16:01

The idea is to be multi-modal, but I've started it with a clojure-esque language that I'm creating. Ultimately it's an editor for developing new languages, and I want to have various syntax options

Misha A 2024-11-09 15:38:21

Can you expand on what is structured for you?

Syntax as in "commas, parens, semicolons" (collections and atoms) is just a part of the story.

Other part is semantic meaning of that syntax.

For example the difference in meaning of tokens in (+ 1 2 3) vs (if 1 2 3) ,

or meaning of vector( [] ) elements in (defn foo [a b] ...) vs (let [a b] ...) .

This is why both "lisp has almost no syntax" and "lisp code is AST" - BS.

Ultimately it's an editor for developing new languages

"editor for new clojure/lisp macros" would be nice test/milestone/challenge for it.

I tried to approach same/similar problem recently as "DSL for custom macros support for Clojure IDE", because condo configs are nightmare: clojurians.slack.com/archives/C06AH8PGS/p1713614069031559

(clojurians-log.clojureverse.org/instaparse/2024-04-20)

Jared Forsyth 2024-11-09 16:16:09

Yeah so my structured editor works at a level between raw text and the AST. I've tried doing structured editors at the AST level, but it ended up being misaligned with the way I wanted to be inputting & manipulating code. So I think that the level that treats (+ 1 2 3) and (if 1 2 3) the same is the right spot for editor manipulation.

And then a language's parser converts this "concrete syntax tree" into an AST

Jared Forsyth 2024-11-09 16:17:58

Yeah I had a great chat Peter Vilter a couple years ago about his datalog stuff, it's very cool!

Misha A 2024-11-09 18:40:23

"paredit for js"?

slurp, barf, wrap in parens/brackets/curlies, swap tokens (left-right, top-bottom)?

Feels like not enough to make new editor. What else you have in mind? (if you don't mind ofc)

Jared Forsyth 2024-11-09 19:37:42

hah so, there's a variety of things going on in the project

  • structured editor is a part of it (I guess I haven't really gotten around to writing out my "why structured editors" thoughts, but one game-changer is persistent addressability in the midst of changes. for an editor to be able to have a durable location for e.g. "the name of the function flatMap " that's not a line/col pair that will break at the slightest touch, unlocks a lot of nice things)
  • jupyter/observable/etc. style super-REPL/literate programming environment for pure functional languages
  • unison-style "terms are referenced by the hash of their contents, stored and synced in a database"
  • a Development Environment for programming languages themselves, making it easy to iterate and play with various aspects of a programming language (compilation targets, execution semantics, type inference algorithms) in relative isolation, as well as enabling the bootstrapping of self-hosted languages
Jared Forsyth 2024-11-09 19:40:51

It used to be "I want to make a programming language that has All The Best Features" and while I was at it I figured I'd make a structured editor for it at the same time, because I've tried making a structured editor for existing languages and concluded that it would work much better if the language (& compiler) were designed with structured editing in mind.... and then I got a little distracted by wanting to make "a minimally-featured language that is capable of self-hosting its own type inference while being nice to use", and so it has morphed into being an Editor Environment that can be used to make a variety of programming languages 🙃

Jared Forsyth 2024-11-09 19:42:08

Thus far the editor has only allowed clojure-style syntax, but the past few days I've been wondering what it would take to open it up to c-style languages, and if I'm going to do that might as well come up with a General Unified Theory of Syntax 😄

Misha A 2024-11-09 19:54:38

It seems to me #1 is at odds with level that treats (+ 1 2 3) and (if 1 2 3) the same is the right spot for editor manipulation because all you get is some-hash[0][1][0][6] (nested array address) or something w/o knowledge what + or defn means.

re unison: YES. designing new lang w/o even giving it a try to be "content-addressable" – ...

it solves/simplifies/amplifies so much later on in the toolchain: deps, version control, diffs/reviews, (structural)editing.

(in my like 4th spare time I try to retrofit content-addressability onto at least a subset of clojure, which too started elsewhere: from custom macros, to kondo-config for it, to "screw it - I'm writing myself a clojure IDE with blackjack", to "might as well make bake in addressability and distribution for source control, because git is both overkill and underwhelming (like any text-files-diffing SCM)")

Jared Forsyth 2024-11-09 19:56:58

Ah so (+ a b c) is actually a map of node-id to node, 0=list(1 2 3 4), 1=id(+, ref=hash of the + function), 2=id(a, ref=hash of the a term) etc

Jared Forsyth 2024-11-09 19:57:44

So the + in (defn + [a b] ...) is addressable as some-toplevel-id : the-loc-of-that-id-node

Jared Forsyth 2024-11-09 19:59:02

which ends up being nicely durable

Jared Forsyth 2024-11-09 20:00:15

Yeah Dion is super cool! I wished they'd produced more about it 😭

Misha A 2024-11-09 20:01:33

so essentially what I wrote? hash[1st][0th][7th] or am I not seeing something?

also how is "hash of a func" different from "hash of a term"?

so you have some "rule" that "1st item in a () list - function call"? that's a semantical knowledge I mentioned

Jared Forsyth 2024-11-09 20:01:53

nope nope

Jared Forsyth 2024-11-09 20:02:39

(sorry talking afk, 1 minute)

Misha A 2024-11-09 20:04:58

at the very least you need to differentiate "new name N " from "reference R ", as in my example defn vs let :

(defn foo [x y] ...)

 R    N    N N



(let [x y] ...)

 R    N R 

and that is semantic knowledge, not just "collections and atoms"

Jared Forsyth 2024-11-09 20:06:21

yeah so in the editor identifiers are by default "unlinked", and there are editor affordances for "linking" an id to a definition

Jared Forsyth 2024-11-09 20:07:20

and the parser provides hints back to the editor about when to provide those affordances

Misha A 2024-11-09 20:07:48
  • constants/literals C

  • scope (which R are known, and which are error))

(defn foo [x y] ...)

 R    N    N N

(quote (defn foo [x y] ...)

 R      C    C    C C
Jared Forsyth 2024-11-09 20:08:20

C is the same as N, no need to distinguish

Jared Forsyth 2024-11-09 20:08:47

Importantly: linking an ID (turning an N into a R) is done by the user, not by some out-of-band algorithm

Jared Forsyth 2024-11-09 20:09:33

So an important difference from unison: I'm not trying to Normalize All The Things

Misha A 2024-11-09 20:09:54

linking is done by the user

are you describing "when user writes grammar for new lang"? or "when user programs in new lang"?

Jared Forsyth 2024-11-09 20:10:09

Writes programs using the new lang

Jared Forsyth 2024-11-09 20:10:18

it's autocomplete that actually means something

Jared Forsyth 2024-11-09 20:12:43

So more realistically, the toplevel (defn a [b] c) probably looks like id=x45r, root: 37, nodes: 37=list(11 3 7 1), 11=id(defn, ref=builtin), 3=id(a, ref=null), 7=array(20), ... . So "the name of that defn" is (x45r, 3)

Misha A 2024-11-09 20:12:58

then you need "scoping rules", or rather "autocomplete needs to know scoping rules". that's again part of semantics of particular list of atoms

(I might have a tunnel vision, because I spend lots of time in with in from the clojure pov)

Jared Forsyth 2024-11-09 20:13:07

hahaha

Jared Forsyth 2024-11-09 20:13:40

Yeah, that's the part where the parser gives autocomplete hints back to the editor

Misha A 2024-11-09 20:13:51

"the name of that defn" - defn or (defn ...) ?

Jared Forsyth 2024-11-09 20:14:02

sorry a is what I meant to be referncing

Jared Forsyth 2024-11-09 20:14:26

the ID the defines the 'name' of the function that is produced by that toplevel

Jared Forsyth 2024-11-09 20:16:35

This is more ~relevant~ interesting when a toplevel can have multiple exports, for example with

(deftype (option a) (some a) (none))

Jared Forsyth 2024-11-09 20:16:58

References to the type constructor some have a durable reference to the id that defines the name of it, so renames are trivial

Misha A 2024-11-09 20:29:43

how scope "spreads" is a semantic too.

in clojure (again, sorry :D) - there are (at least) parallel scope, forward sequential scope, backward sequential scope:

here, numbers is sequence of scope propagation (higher number gets its scope from prev number):

forward + backward example:

0

 1   2

        3

(let [a x b a] [a b])

      4     

            5

          6    7

                8 8 ;; a and b have parallel scope at this point

 

parallel example:

0

 1       2

          4 3 4 3  5

                    6 6 

(binding [a 1 b 2] [a b])  

;;The new bindings are made in parallel (unlike let);

also notice, that in let , b ejects/exports its cope from vector to body [a b] , but body does not export scope outside let (propagation stops).

So spread direction is based on the meaning of first symbol, and the fact that 1st symbol meaning is important - is a higher level semantic too

Misha A 2024-11-09 20:32:10

One instance of scope export is created global definition: in (defn foo [a b] body) defn exports foo to the global scope, but not a b or body .

which is solely semantics of defn

Jared Forsyth 2024-11-09 20:32:14

So locals aren't locked in

Jared Forsyth 2024-11-09 20:32:32

Also the parser tells the editor what is exported

Misha A 2024-11-09 20:32:48

so parser knows what's up (which list el is local which is not), because you backed in some semantics in it (for c-like langs - defined a set of keywords and what they mean: if def for while)

Misha A 2024-11-09 20:33:42

but if you allow user-defined macros in your lang - you need to provide a way for user to let parser know what's up

Jared Forsyth 2024-11-09 20:36:17

So macros work on the cst, and are expanded before the parser operates

Misha A 2024-11-09 20:38:45

basically you describe(bake in) N of special forms and their semantics during lang-design-phase, and then rely on macroexpand for autocomplete?

Jared Forsyth 2024-11-09 20:47:33

Oh yeah so macros also can report autocomplete hints

Misha A 2024-11-09 20:49:35

C is the same as N, no need to distinguish

literal symbol defn is not the same as defn which is meant to be looked up and resolved as e.g. clojure.core/defn

so either you need to prompt user on every word "is it a ref or is it static/literal?", or forbid literals, or rely on semantics of something in the text before the word, again, in clojure: ' or quote , which is a semantic not just "colls and atoms"

Misha A 2024-11-09 20:53:55

this is all long winded way to say that "syntax families" seem to be incomplete w/o mentioning scope propagation rules and semantics

Jared Forsyth 2024-11-09 21:02:57

So when typing defn, if it autocompletes to link then it is R otherwise it's C

Jared Forsyth 2024-11-09 21:03:18

Macros operate on Rs mostly tbh

Jared Forsyth 2024-11-09 21:04:25

But yeah only global scope references are linked

Misha A 2024-11-09 21:04:28

mostly≠only )

Jared Forsyth 2024-11-09 21:04:51

Also macros don't have access to any environment to resolve things

Jared Forsyth 2024-11-09 21:05:38

The only time they'd use a C is for a numeric literal or as the export-name for a new definition

Jared Forsyth 2024-11-09 21:07:05

Either a macro consumes core/defn as an R, or has it referenced in its definition, or it doesn't have access to it

Misha A 2024-11-09 21:07:22

how macro knows it's an R when there is no env access to look it up?

Jared Forsyth 2024-11-09 21:07:33

an attribute on the node

Jared Forsyth 2024-11-09 21:07:57

Whether node.ref is null

Misha A 2024-11-09 21:07:58

so macro receives already resolved things?

Jared Forsyth 2024-11-09 21:08:07

Yup

Jared Forsyth 2024-11-09 21:08:39

Well it can also access "terms associated with resolved things it has received"

Jared Forsyth 2024-11-09 21:08:58

Where term associations are explicitly defined as a first class thing

Jared Forsyth 2024-11-09 21:10:07

This is all critical for term hashes to be useful. Can't depend on "the whole environment"

Misha A 2024-11-09 21:13:26

ok, defn is a macro, it receives foo and a b

all 3 foo a b exist in global scope (= can be resolved, and are resolved before being passed into macro)

now defn throws away resolution and assigns new roles of global to foo and local to a and b ?

Jared Forsyth 2024-11-09 21:14:25

So def isn't a macro

Misha A 2024-11-09 21:14:33

or foo a b are not in the globals, resolution resolves to 'unknown' and passes that to defn ?

Jared Forsyth 2024-11-09 21:14:34

Gotta be built in

Jared Forsyth 2024-11-09 21:14:55

Macros bottom out to def/def type/etc

Misha A 2024-11-09 21:15:47

by built in you mean semantics (scope propagation rules, locals/globals export) baked in into "parser" ?

Misha A 2024-11-09 21:17:06

ok, but what about defn being macro in clojure (bottoms out to def ), and all those prismatic.schema/defschema etc. basically any macro exporting new global?

Jared Forsyth 2024-11-09 21:17:08

Mean it (def) can't be a macro

Jared Forsyth 2024-11-09 21:17:33

defn produces a def, which the parser determines produces an export

Jared Forsyth 2024-11-09 21:18:28

This allows different parsers (e.g. languages) to have different forms for defining things

Misha A 2024-11-09 21:18:43

defn produces a def, which the parser determines produces an export

yes, but defn knows what is new global, and what is args (new locals). and you resolve them before defn gets them

Jared Forsyth 2024-11-09 21:19:14

"new global" is just an id with ref=null

Jared Forsyth 2024-11-09 21:19:42

'fn' also can't be a macro

Misha A 2024-11-09 21:20:52

does editor show it as an error? how does editor know it's not an error, and foo is ok to be unresolved at this particular place: second token inside (defn foo ...) list

Jared Forsyth 2024-11-09 21:21:42

Parser knows what ids need to be resolved

Misha A 2024-11-09 21:21:48

macroexpand + "source map" from exported new global back to foo in the (defn foo)?

Jared Forsyth 2024-11-09 21:22:02

No need to source map :) durable ids

Misha A 2024-11-09 21:22:26

I mean conceptually

Misha A 2024-11-09 21:23:24

"expand, and see that id=7 goes from unresolved to export-new-global, and its all good"?

Jared Forsyth 2024-11-09 21:27:51

I mean it's a parser error to use an id with ref=null as an expression if it's not resolvable with local scope

Jared Forsyth 2024-11-09 21:28:54

I can imagine a parser using an unresolved I'd in other ways that it would determine are valid

Jared Forsyth 2024-11-09 21:29:31

In fact it would be a parser error to use a resolved id as the "name of what I'm exporting"

Jared Forsyth 2024-11-09 21:30:47

So it's not a "how do I ensure unresolved ids eventually have a home" problem, it's more generally "do all these nodes make sense"

Misha A 2024-11-09 21:33:16

In fact it would be a parser error to use a resolved id as the "name of what I'm exporting"

can't redefine things this way.

(def foo 1)

(def foo 2) ;; exports already resolved(able) global foo
Jared Forsyth 2024-11-09 21:34:36

Yeah so no name conflicts allowed in the same module

Misha A 2024-11-09 21:35:24
(def elsewhere/foo 1) ;)
Jared Forsyth 2024-11-09 21:36:45

And it wouldn't autocomplete to resolve that id

Misha A 2024-11-09 21:37:23

because it knows from hardcoded knowledge "no qualified symbols here"?

Jared Forsyth 2024-11-09 21:37:39

Parser decides what autocompletes

Misha A 2024-11-09 21:38:24

I understand, I just try to zero in on "based on what"

Jared Forsyth 2024-11-09 21:38:57

When the parser is parsing, and sees the sibling to a 'def' in this case

Jared Forsyth 2024-11-09 21:39:07

At the top level

Jared Forsyth 2024-11-09 21:39:31

Btw ids with refs are underlined

Jared Forsyth 2024-11-09 21:39:46

Visually distinct

Misha A 2024-11-09 21:39:49

circling back to "IDE for defining new languages": that initial grammar/lang-description needs to provide that info for parser/autocomplete.

Jared Forsyth 2024-11-09 21:40:23

Yeah so the base lang is just raw js lol

Jared Forsyth 2024-11-09 21:40:30

In a big ol string literal

Misha A 2024-11-09 21:40:35

😄

Jared Forsyth 2024-11-09 21:40:44

And then you use that to make other languages

Misha A 2024-11-09 21:41:47

does that base-lang restricts what semantics are un/available to new-langs? or is it to academic or hard to tell atm?

Jared Forsyth 2024-11-09 22:10:09

So the base lang doesn't produce any restrictions

Jared Forsyth 2024-11-09 22:12:10

The nature of the editor and such does produce limits though. For example, macros don't have access to the environment. The parser and compiler don't even have global access. Dependency graphs are calculated by the editor.

Jared Forsyth 2024-11-09 22:13:26

Also impurity is a no go for the repl to make sense

Paul Tarvydas 2024-11-09 21:34:21

Thinking about multi-paradigm programming. Why FP isn't enough, IMO... programmingsimplicity.substack.com/p/single-paradigm-vs-multi-paradigm-d7e?r=1egdky

📝 Single-Paradigm vs. Multi-Paradigm Thinking (Slight Return)

Managing Programming Complexity 2024-11-09

Jared Forsyth 2024-11-09 23:30:34

It was a little jarring to have it start off saying "people impose weird constraints on computing like insisting on functions as a base when assembly doesn't have them" and then a little while later essentially say ~"let's pretend that shared memory is literally impossible"~ "we'll consider the concurrency as only being between two machines separated by a single wire, ignoring the existence of multi-core CPUs" That seems like a weird constraint? Or maybe I'm misunderstanding. Aren't those two sections essentially at odds?

Jared Forsyth 2024-11-09 23:32:12

I'd be very interested to learn about non-function-based computing though. Sounds fascinating.