Live data from Hacker News

McCarthy's Ambiguous Operator (2005)

randomhacks.net

11–20 of 43 posts

Re: McCarthy's Ambiguous Operator (2005)

#11
Regarding the ruby example, thinking about the performance of such a (non-)feature makes my head spin. Not to mention the possibility of side-effects wrecking the program state and the complexity of debugging code like that without making Rubys error handling aware of such a construct.

All in all, this seems like a fun hack, but a catastrophically bad idea in practice.

Re: McCarthy's Ambiguous Operator (2005)

#12
post #3

amb without side-effects can also be expressed in terms of the List monad, e.g.: do x which can be equivalently reworded as the list comprehension: [(x, y) | x i.e., the underlying structure of amb without side-effects can also be understood as just a search of the input space for values that fulfill some criteria, rather than as backtracking via continuations.

This analogy misses something fundamental. See how the assignments to x and y are of very different character than the predicate? Notice the return path (`else []`). `amb` doesn't need this. `amb` merges the assignments and predicate - this is important because you may not know your argument count statically, i.e. if your arguments are a list. But beyond that, `amb` may backtrack an arbitrarily deep call stack, so th…

We can implement it with a single `amb` function, too (I took some shortcuts that might have hidden some of the nature of the implementation)!

    -- Lifts a list into Amb.
    amb :: [a] -> Amb a
If we assume Amb is just List, then:

    amb = id
If we write the example in the original article in desugared style, we get:

    amb [1, 2, 3] >>= \x ->
    amb [4, 5, 6] >>= \y ->
    if x * y /= 8 then amb [] else pure () >>
    pure (x, y)
(we are forced to use an awkward condition with `pure ()` on the else branch when calling `amb` because Haskell requires us to return values on all branches. We can rewrite it equivalently in terms of `when` to hide that detail:

    amb [1, 2, 3] >>= \x ->
    amb [4, 5, 6] >>= \y ->
    when (x * y /= 8) (amb []) >>
    pure (x, y)
It now looks more similar to the original example.)

It ends up looking like continuation-passing style: conceptually, if we encounter `amb []` in the nested function, the nested computation ends and we start examining the next value in the list values being assigned to `x`: the implementation given in the original post does end up using callcc, so with some imagination you might be able to derive some kind of equivalence here :)

Re: McCarthy's Ambiguous Operator (2005)

#13
post #10
post #6

SICP spends many pages on this.

right, actually one of the topics were i stopped on my first readings. https://mitpress.mit.edu/sites/default/files/sicp/full-text/...

Contextualizing this with Hoare's work on pre and post conditions helped me to understand this chapter. Predication is fundamental to advanced computer science.

Re: McCarthy's Ambiguous Operator (2005)

#14
I suppose this is a good time to point out the toy Lisp interpreter I wrote a while ago: http://patient0.github.io/FirstClassLisp/

It supports first class macros, continuations, and has the amb "operator" (implemented using a macro of course!): https://github.com/Patient0/FirstClassLisp/blob/master/Lisp/...

Re: McCarthy's Ambiguous Operator (2005)

#15
post #3

amb without side-effects can also be expressed in terms of the List monad, e.g.: do x which can be equivalently reworded as the list comprehension: [(x, y) | x i.e., the underlying structure of amb without side-effects can also be understood as just a search of the input space for values that fulfill some criteria, rather than as backtracking via continuations.

This analogy misses something fundamental. See how the assignments to x and y are of very different character than the predicate? Notice the return path (`else []`). `amb` doesn't need this. `amb` merges the assignments and predicate - this is important because you may not know your argument count statically, i.e. if your arguments are a list. But beyond that, `amb` may backtrack an arbitrarily deep call stack, so th…

    foo = do
        x 

Re: McCarthy's Ambiguous Operator (2005)

#16
post #12

Earlier quoted context omitted.

This analogy misses something fundamental. See how the assignments to x and y are of very different character than the predicate? Notice the return path (`else []`). `amb` doesn't need this. `amb` merges the assignments and predicate - this is important because you may not know your argument count statically, i.e. if your arguments are a list. But beyond that, `amb` may backtrack an arbitrarily deep call stack, so th…

We can implement it with a single `amb` function, too (I took some shortcuts that might have hidden some of the nature of the implementation)! -- Lifts a list into Amb. amb :: [a] -> Amb a If we assume Amb is just List, then: amb = id If we write the example in the original article in desugared style, we get: amb [1, 2, 3] >>= \x -> amb [4, 5, 6] >>= \y -> if x * y /= 8 then amb [] else pure () >> pure (x, y) (we are…

That's not an amb function. That's a builtin type that's inherently amb-y.

You still get credit for solving the basic problem but let's not be misleading.

Also don't forget to call head.

Re: McCarthy's Ambiguous Operator (2005)

#17
post #3

amb without side-effects can also be expressed in terms of the List monad, e.g.: do x which can be equivalently reworded as the list comprehension: [(x, y) | x i.e., the underlying structure of amb without side-effects can also be understood as just a search of the input space for values that fulfill some criteria, rather than as backtracking via continuations.

This analogy misses something fundamental. See how the assignments to x and y are of very different character than the predicate? Notice the return path (`else []`). `amb` doesn't need this. `amb` merges the assignments and predicate - this is important because you may not know your argument count statically, i.e. if your arguments are a list. But beyond that, `amb` may backtrack an arbitrarily deep call stack, so th…

Hmmm, I think there is some subtlety here in terms of the way an empty list is used in Haskell. This paper by Wadler from the 80s (predating monads) discusses the functional lore of the combinator approach to parsing, and it significantly leverages the properties of [] to represent failure. It's a "semipredicate" type of situation, where the return value is either a list or failure; conveniently, the empty list isn't one of the possible success values. So if I understand the Haskell idiom correctly, it's a bit of a hack which is inherent in the heritage of the list monad. (NB there's nothing about raw lists per se that means they form a nondeterministic choice monad—that's a culturally contingent Haskell thing.)

https://rkrishnan.org/files/wadler-1985.pdf

Re: McCarthy's Ambiguous Operator (2005)

#18

The McCarthy '63 paper referred to seems to be here: http://www-formal.stanford.edu/jmc/basis1/basis1.html

A brilliant paper. IMHO one of the cornerstones of computer science. I can warmly recommend it. 'pg has a good explanation of it here: http://www.paulgraham.com/rootsoflisp.html?viewfullsite=1 (Windows users might want to use an online converter to convert it from Postscript to PDF.)

Re: McCarthy's Ambiguous Operator (2005)

#20
Author here. It's fun to see this post 14 years later!

If you like this, you might also enjoy:

- Prolog: This is an entire language built around choice and backtracking. https://en.wikipedia.org/wiki/Prolog

- The Reasoned Schemer, which builds a Prolog-in-Scheme from first principles. This book is excellent, or even mind-blowing if you're never seen this before. https://mitpress.mit.edu/books/reasoned-schemer

- Oz/Mozart: This language takes the basic idea even further, adding concurrency and constraint solving. There's an older book about this that's nice: https://www.info.ucl.ac.be/~pvr/book.html It feels like Oz was an evolutionary dead end, but an interesting one.

- Probability monads. You can extend this general idea to include probability distributions over values. I have an older series of blog posts at http://www.randomhacks.net/probability-monads/ that explains this idea piece-by-piece, but there's been a ton of research in the past decade on probabilistic languages: http://www.probabilistic-programming.org/wiki/Home

It's definitely a fun topic!

Post reply on HN