Fork me on GitHub

Hey I have a question on the design of Meander for which I would like to know your take, @noprompt. It's not a suggestion on my part, it's more understanding why things work in a certain way. Let's take as a starting point an example I had last time:


You can see that, in the part that does the construction of the new pattern, I have to call meander/app to signify my intent of applying the function, because (function args) would have been read literally, as data. My question would be, why is meander/app not the default meaning for applications in the RHS? You could still quote things explicitly when you want a list, and it seems to me that's the most common case. But I know from your interview on youtube that you thought about semantics quite a bit, hence my curiosity 🙂


@meditans m/app is not the default on the RHS because the goal for substitution patterns is to be the dual of matching patterns and share the same syntax with them (though for epsilon this is currently not the case but for zeta it is). So if, for substitution, (p_fn & p_args) is to mean apply substitution to p_fn and p_args to get f and args, then clojure.core/apply f to args, there would need to be a suitable dual semantic for matching. I think there is one that could make “sense” and I’m going to ramble on about how m/app works in zeta, point out that m/app in epsilon is a little janky, some other thoughts on the topic, and hope for the best. In zeta, (m/app p_fn p_args p_object) matching means yield objects of the pattern p_fn and objects of the pattern p_args to get fn and args respectively, then apply fn to the object being matched and args the return of which is matched against p_object.

;; epsilon
(m/match 1 (m/app vector [?x]) ?x)
;;                ❶    ❷
;; => 1
;; ❶ Function is a host expression without ~
;; ❷ Additional arguments must be supplied in host expression

;; zeta
(m/match 1 (m/app ~vector [] [?x]))
;;                ❶      ❷
;; => 1
;; ❶ Function is a pattern
;; ❷ Additional arguments can be supplied with patterns
(m/app p_fn p_args p_object) for substitution means yield objects of the pattern p_fn and objects of the pattern p_args to get fn and args respectively, then apply fn to args to get the object being yielded which must match the pattern p_object.
;; epsilon
(let [?x 1]
  (m/subst (m/app vector ?x ?x)))
;;         ❸     ❶      ❷ ❷
;; => [1 1]
;; ❶ Function is a host expression without ~
;; ❷ Remaining pattern arguments assumed to the arguments to the
;;   function which is completely different from match
;;   where the remaining arguments are semantically equivalent to
;;   `m/and`
;; ❸ Not possible to constrain return with a pattern

;; zeta
(m/subst (m/app ~vector [?x ?x] _) {'?x 1})
;;              ❶      ❷      ❸
;; => [1 1]
;; ❶ Function is a pattern
;; ❷ Explicit arguments
;; ❸ Possible to constrain return with a pattern
As you can see from these examples, the syntax and semantics m/app in epsilon is a trashfire as it assumes the function is a host expression, arguments are passed to the function in different ways, and substitution lacks the ability to constrain the return. zeta on the other hand has consistent syntax and the only semantic difference is that of matching/substitution. Going back to your original question, I think it would be possible to have an m/app everywhere style where patterns are of the form (p_fn p_args p_return) retaining some of the primitive operators and desugaring to m/app.
(m/rewrite (list 1 2 3)
  (m/and (~sequential? [] true)
         (~vec [] [?x ?y ?z]))
  (~+ [?x ?y ?z] _))
;; => 6
Alternatively, if you have as primitives patterns which represent the duals of construction and deconstruction for each of the primitive data types which exist, you would have something that could serve this purpose for many things.
(m/rewrite (list 1 2 3)
  (m/cons ?x (m/cata ?y))
  (m/conj ?y ?x)

  _ [])
;; => [3 2 1]

(m/rewrite [1 2 3]
  (m/conj (m/cata ?x) ?y)
  (m/cons ?y ?x)

  _ ())
;; => (3 2 1)

❤️ 3
🙌 3

thanks for the very detailed explanation @noprompt, can't wait to see zeta!


Remind me never to write a long reply in my editor then paste it into Slack thinking it’ll work out. 😂