Paul Tarvydas 2024-11-04 02:24:28 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
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...
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’.)
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).
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)
Jared Forsyth 2024-11-09 13:22:23 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 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
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
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: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:40 Yeah, that's the part where the parser gives autocomplete hints back to the editor
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
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?
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: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: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
?
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
?
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: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
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
Misha A 2024-11-09 21:21:48 macroexpand + "source map" from exported new global back to foo
in the (defn foo)?
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
Misha A 2024-11-09 21:37:23 because it knows from hardcoded knowledge "no qualified symbols here"?
Jared Forsyth 2024-11-09 21:38:57 When the parser is parsing, and sees the sibling to a 'def' in this case
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.
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: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.
Paul Tarvydas 2024-11-09 21:34:21 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.