Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Destructuring vectors syntax #1844

Closed
ashgti opened this issue Feb 14, 2012 · 9 comments
Closed

RFC: Destructuring vectors syntax #1844

ashgti opened this issue Feb 14, 2012 · 9 comments
Labels
A-frontend Area: frontend (errors, parsing and HIR) C-enhancement Category: An issue proposing an enhancement or a PR with one.

Comments

@ashgti
Copy link

ashgti commented Feb 14, 2012

Currently its possible to destructure tuples, records, and enums in let's and alt patterns. It could be useful to also destructure vectors as well, plus it would feel more consistent.

The obvious goal would be to do something like this:

fn foldr<A, B>(func: fn(A, B) -> B,
               initValue: B,
               vals: [A]) -> B {
  alt vals { 
    [] { ret initValue; }
    [head, tail...]  { ret foldr(func, func(initValue, head), tail); }
  }
}

I am not sure of the syntax exactly but it would be useful to be able to destructure vectors at least in some way.

Another example of why this might be useful:

fn ever_other_element<A>(vals: [A]) -> [A] {
  alt vals {
    [head, _, rest...] { ret [head] + ever_other_element(rest); }
    [head, _] {  ret [head]; }
    _ { ret []; }
  }
}

Could be expressed as:

fn ever_other_element<A>(vals: [A]) -> [A] {
  if vec::len(vals) > 2 {
    let head = vals[0],
        rest = vec::tail(vec::tail(vals));
    ret [head] + ever_other_element(rest);
  }
  else if vect::len(vals) == 2 {
    let head = vals[0];
    ret [head];
  }
  else {
    ret [];
  }
}

In my samples I am assuming that in this expression: [head, tail...]; head matches 1 element and tail matches 0+ elements.

But, the difference is just that using pattern matching makes this a lot more readable. If this should apply to lists instead of vectors, let me know, but list's are in the core:: they are in std:: so, my assumption is that it makes more sense for vec to work this way.

@bstrie
Copy link
Contributor

bstrie commented Feb 15, 2012

This RFC stems from this StackOverflow question: http://stackoverflow.com/questions/9282805/rust-pattern-matching-over-a-vector

Personally, I think it would be really useful if alts were able to reason about vectors in the manner shown in the first example here. Testing for an empty list using [] { ... } seems much more intuitive and concise than x if x == [] { ... }, and the tail... syntax (which feels familiar to rest parameters in Harmony) seems to be somewhat analogous to the ... in the macro-by-example syntax.

@ashgti
Copy link
Author

ashgti commented Feb 15, 2012

I kinda made up the syntax I was using, so feel free to change/modify/critique it as much as you guys want. I simply wrote it that way to express the idea.

Haskell has a nice way of doing it by expressing the inverse of what you want, eg. a list is constructed from a number of cons (represented as an infix :) with a [] at the end. So: [1, 2, 3] == 1:2:3:[] and in the pattern matching syntax you can express the inverse operations you want applied to the structure. So, saying x:xs really means just undo that first cons. Which is ultimately nice because the syntax you use in the pattern matching is the same syntax you use in other places, just reversed.

Ultimately, these are all operations that could easily be defined with a few ifs and maybe a call to find the length of the vector, but using a pattern to express the concept feels more concise.

@amunra
Copy link

amunra commented Mar 17, 2012

+1, absolutely.

I was staring at the compiler trying to do similar things, only to realise that it hasn't yet been implemented!

I was attempting something like this:

alt xs {
  [_,"foo"] {"bar"}
  _         {"pub"}
}

@nikomatsakis
Copy link
Contributor

Something like this would be nice to have, for sure. If we had slices as @graydon recently proposed, then the ... syntax would be cheap.

@catamorphism
Copy link
Contributor

Seems like #1799 blocks this.

@catamorphism
Copy link
Contributor

Since we have slices, this is no longer blocked.

@ghost
Copy link

ghost commented Nov 29, 2012

I've been looking at this (https://github.com/fawek/rust/commits/vector-destructuring) as a way to familiarize myself with the compiler. As the thread's been inactive for a while, I'd like to make sure that the current consensus is still that it would be good to have this.

Based on the original two examples, the above branch compiles the following:

fn foldl<T, U: Copy>(
    values: &[T],
    initial: U,
    function: &fn(partial: U, element: &T) -> U
) -> U {
    match values {
        [head, tail...] => foldl(tail, function(initial, &head), function),
        _ => copy initial
    }
}

fn main() {
    let x = [1, 2, 3, 4, 5];
    let y = foldl(x, 1, |a, b| a * *b);
    io::println(fmt!("%d", y));
}

And the following:

fn every_other_element<T: Copy>(
    values: &[T]
) -> ~[T] {
    match values {
        [head, _, tail...] => ~[head] + every_other_element(tail),
        [head] => ~[head],
        _ => ~[]
    }
}

fn main() {
    let x = ["foo", "bar", "baz", "abc", "def"];
    let y = every_other_element(x);
    for y.each |s| {
        io::println(fmt!("%s", *s));
    }
}

One missing bit I'm still looking at is proper detection of exhaustiveness. As it is now, the following wouldn't compile:

fn every_other_element<T: Copy>(
    values: &[T]
) -> ~[T] {
    match values {
        [head, _, tail...] => ~[head] + every_other_element(tail),
        [head] => ~[head],
        [] => ~[]
    }
}

Which might lead to confusion.

I'll submit a proper PR with tests if the community still agrees on the syntax.

@ghost ghost mentioned this issue Dec 1, 2012
@ashgti
Copy link
Author

ashgti commented Dec 28, 2012

Pull request 4091 seems to of landed, which implements this RFC, I think this can be closed?

@ghost
Copy link

ghost commented Dec 28, 2012

I think it can. I just realized there's no documentation for this at the moment. I'll write some.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-frontend Area: frontend (errors, parsing and HIR) C-enhancement Category: An issue proposing an enhancement or a PR with one.
Projects
None yet
Development

No branches or pull requests

5 participants