"David A. Black" <dblack / wobblini.net> wrote

> rather than simply
> asking an object to do something at the moment you want it to do that
> thing, you're introducing a wrapper/notation mechanism

Not always true. Below I've dropped many of the ':' as I think they are not
syntactically essential.

    [x,y] = obj
is the same as
    x = obj[0]
    y = obj[1]
So I ask obj to 'do' its [0], [1] right where I want it. Variables are bound
to results.

    (m1(obj1) x, m2(obj2) y) = obj
is the same as
    x = obj.m1(obj1)
    y = obj.m2(obj2)
Again, I ask obj to do it's m1(), m2() right where I want it. Variables are
bound to results.

    x = obj
    Object x = obj
are identical.

Now
    T x = obj
would be
    x = obj
    assert x.is_of_type(T)
    # Object#is_of_type would handle named types, type patterns, classes,
etc.

So:
    (m1(), m2()) x = obj
declares that m1() and m2() are needed, but does not invoke them since there
are no variables to bind to obj.m1(), obj.m2(). This is like regexs cases
where you want a match but don't care for a variable to be bound. Hence,
besides binding x to full obj, it also includes an assertion
    x = obj
    assert( x.is_of_type(::(m1(), m2())
    # i.e. assert( x.respond_to?(:m1) && x.respond_to?(:m2) )

And such assertions might not be needed if other type info was known (either
simple type propagation, or more sophisticated type inference, to the extent
possible in Ruby):
    def f (T obj)
        x = obj # so x.is_of_type T
        T y = x
        # no type assertion needed for T y = obj

> ...[can] prevent you from getting to that point at all
> under certain conditions.

Yes, this pattern matching can fail, and depending on context might
sometimes raise errors e.g. In places like a case-when or if- statement a
match can fail without raising any error; its just a boolean check, with the
added bonus of variable bindings from a successful match. In a regular
assignment it can fail to match and will raise the same errors as the
equivalent regular ruby code.
- [x,y]=obj : can raise same errors as x=obj[0], y=obj[1]
- (m1(), m2()) x = obj can raise errors from 'assert x.respond_to...'

> That doesn't make it good or bad in itself -- it
> would just be clearer, I think, not to label it duck typing.

I see. Some usages of it are intentionally closer to explicit type
declarations. Maybe 'duck type declarations'?

> Maybe there's some way to develop the pattern-matching idea but not
> necessarily put it all in the method signature.

Yes. For example we could put this info right after the def f(x,y) so the
def line is uncluttered. Something like this would be ok.
def f ( x, y )
    (m1(), m2()) x
    # or T x,  if T was named type defined as m1(), m2()
In this case Ruby should associate the type-declared x with the parameter x,
rather than a new local variable. Would you agree?

But this would not allow deconstructing (taking apart and pattern matching)
anonymous args.
    def f [x,y]
would instead become
    def f obj
        obj[0] x # new local var? appears in signature?
        obj[1] y # new local var? appears in signature?
Do you think there should be some way to treat the "obj[0] x" as part of the
signature? I am certain that
    def f [start, end]
communicates much better signature info, with zero overhead, than
    def f start_end

> Could incremental
> things, like allowing #respond_to? to take an array or multiple
> arguments, work in that direction?

I see where you are going. Do you mean:
def f(x)
    x.respond_to? :m1, :m2, :m3

I did want to somehow distinguish patterns used as type declarations from a
regular (and overridable!) method call like respond_to. There may be other
alternatives to "punctuation", but how would using 'normal' respond_to would
for this purpose?

Cheers.