1. 11
  1.  

  2. 4

    For comparison, the equivalent in Rust:

    use std::num::Zero;
    
    fn sum_slice<T: Zero>(xs: &[T]) -> T {
        let zero: T = Zero::zero();
        xs.iter().fold(zero, |x, y| { x + *y })
    }
    
    let xs8 = [0i8, 1, 4, 8];
    let xs32 = [0i32, 1, 4, 8];
    sum_slice(xs8.as_slice());
    sum_slice(xs32.as_slice());
    

    I think we can do better in 2014 than pass typeclass dictionaries around by hand.

    1. 2

      I’m kind of baffled that that even compiles because your sum_slice doesn’t include an Add constraint, which should be necessary for using +. But indeed it does. For demonstration, here’s a program that will fail to compile because T satisfies Zero but not Add:

      use std::num::Zero;
      
      #[deriving(Show)]
      struct MyNum(int);
      
      impl Zero for MyNum {
          fn zero() -> MyNum { MyNum(0) }
          fn is_zero(&self) -> bool {
              0 == { let MyNum(x) = *self; x }
          }
      }
      fn sum_slice<T: Zero>(xs: &[T]) -> T {
          let zero: T = Zero::zero();
          xs.iter().fold(zero, |x, y| { x + *y })
      }
      fn main() {
          let xs: Vec<MyNum> = vec![1, 2, 3].move_iter().map(MyNum).collect();
          println!("{}", sum_slice(xs.as_slice()));
      }
      

      Which is what I’d expect. If you add an implementation for Add, then it compiles again!

      impl Add<MyNum, MyNum> for MyNum {
          fn add(&self, rhs: &MyNum) -> MyNum {
              let (MyNum(lhs), MyNum(rhs)) = (*self, *rhs);
              MyNum(lhs + rhs)
          }
      }
      

      Freaky.


      Bah, ignore me. The doco for the Zero trait was somehow hiding the fact that it is already constrained with Add<Self, Self>. I didn’t see it until I looked at the source. Weird.


      For those following at home, sum_slice already has a trait AdditiveIterator in std::iter. It seems the Add<A, A> constraint there is redundant since Zero is specified.

      1. 4

        Indeed, I was as surprised as you to see I didn’t need the Add bound on T. Oddly enough, the Add<Self, Self> supertrait is shown in the docs for core::num::Zero but not std::num::Zero. Opened an issue here: https://github.com/mozilla/rust/issues/14636

    2. 2

      I’m confused why Num should be a functor.. by all rights it should be a signature. The equivalent SML would be

       signature NUM = sig
          eqtype num
          val zero : num
          ....
       end
       functor Add(M : NUM) = struct
          open M
          fun accumulate(array) = foldl plus zero array
       end
      
      1. 1

        In myrddin:

        generic add = {sl : @a::(numeric,integral)[:]
             var x = 0 /* 0 is of type @t::(numeric,integral) */
             for v in sl
                 x += v
             ;;
         }
        
         const main = {
             const i8 : int8[:] = [1,2,3,4,5][:]
             const i32 : int32[:] = [1,2,3,4,5][:]
             sum(i8)
             sum(i32)
        }
        

        This doesn’t pass dictionaries, though. It specializes fully at compile time. So, not quite equivalent. (I really need to figure out dynamic dispatch/late binding one of these days…)

        1. 1

          In theory once you can extend protocols with methods you can get a lot closer to Haskell-like type classes. The type class instance gets replaced with self. Anything Monoid-like will have a .concat method, etc.

          This is similar to extension methods on interfaces in C#. API discoverability is amazing once you have this. Imagine anything lens-like having all of the usable lens methods auto-complete after you type the dot. C# developers are already spoiled with this feature.

          One of the developers said they’ve design Swift protocols with this in mind, so there’s some hope yet.