>> What would be Rust alternative, except passing the errors all around
>> parser? Doesn't it grow parser code by at least 50%?

> Haskell has no exception, it has Monads instead. Rust reuses Option and 
> Result (Maybe and Either in Haskell) with great success.

For all my love to Haskell, I wouldn't consider it an example of a
practical programming language. I mean, there aren't many projects
written in Haskell of a size larger than GHC.

>> Why not make a trait BaseString and apply all the operations to it?
> It's "heavier", in several ways.
> [...]
> In short, Slice is just a superior alternative.

I agree that it is conceptually simpler. But compare code, required to
read one integer from a file in Python, C++ and Rust:

a = int(f.read_line())

int a;
f >> a;

let a: int = from_str(f.read_line().unwrap().as_slice().trim()).unwrap();

Two unwrap's are caused by the lack of exceptions, as_slice is to
convert String -> str, trim is because from_str doesn't skip
whitespace (I suppose, the last one is non-essential).


On Thu, May 29, 2014 at 1:27 PM, Matthieu Monrocq
<[email protected]> wrote:
>
>
>
> On Thu, May 29, 2014 at 8:32 PM, Oleg Eterevsky <[email protected]> wrote:
>>
>> > Overloaded functions are unnecessary in Rust. In C++ they are heavily
>> > used because of templates use duck-typing, but Rust uses explicit 
>> > interfaces
>> > (typeclass-like) which makes it unnecessary.
>>
>> I agree. This is one of the main use cases for overloaded functions in
>> C++, and it is irrelevant to Rust. Considering this, I'd say, that
>> introducing optional named parameters might be a better solution.
>>
>> > let mut result = vec!(); // type of "result" is "Vec<...>" which is only
>> > partially known
>>
>> > for x in some_range.iter() {
>> >        doit(result, *x);    // there is a single "doit" in scope, whose
>> > arguments is "Vec<int>" thus this is the type of "result"
>> >  }
>>
>> To be honest, such a non-local type inference creeps me out. It might
>> be an unending source of errors. While it probably can be used in some
>> quick and dirty code snippets, I would never commit such code to a
>> repository of any self-respecting project.
>
>
> Well, I don't find it creepy. I suppose it's just a matter of habit. It
> seems a bit weird, at first, but even after just a couple of days it just
> feels normal again (at least it did for me).
>
> Remark that it is local: local to the method. It's not at the level of
> Haskell where the very signature of a method can be inferred (which gives
> rise to infuriating error messages).
>
> All in all, it seems to strike a good balance.
>
>>
>>
>> > Named parameters and default values are just syntactic sugar; they might
>> > feel handy, indeed, but in general they can be emulated => Rust already has
>> > named parameters for "struct" initialization, and it also has functional
>> > update for those "struct" thus as long as you are willing to add one more
>> > symbol:
>>
>> While it is indeed a syntactic sugar, I believe that it's an important
>> one. (You may argue that all of Python is just syntactic sugar over
>> some dictionary operations and all of Haskell is syntactic sugar over
>> linked lists.) I can't imagine anyone adding an explicit struct type
>> for each and every function that might be expanded in future, or just
>> has optional arguments.
>>
>> >> 2. What about exceptions?
>> > As mentioned, the issue is about recovery: you modified a lot of stuff
>> > in your "try" that is still accessible afterward, is it in a safe state ? 
>> > is
>> > it in sensible state ? No one knows, and your compiler does not care. Tasks
>> > enforce explicitness about what is modified, which helps in auditing.
>>
>> Just to make sure I understand how it all should work. Suppose I have
>> a parser, that raises ParseError. I might use it like this:
>>
>> let something = Something::new();
>>
>> try {
>>   something.parse_str("...");
>> } catch ParseError as e {
>>   println!("ParseError at {}:{}", e.row, e.col);
>>   return;
>> }
>>
>> What would be Rust alternative, except passing the errors all around
>> parser? Doesn't it grow parser code by at least 50%?
>
>
> Haskell has no exception, it has Monads instead. Rust reuses Option and
> Result (Maybe and Either in Haskell) with great success.
>
>>
>>
>> >> 3. It seems like almost any string operation requires as_slice().
>> >> Can't various string methods be also implemented for String?
>>
>> > It's the lowest common denominator approach, a slice is cheap (no memory
>> > allocation) and universal (may refer either to heap allocated buffer or
>> > static buffer).
>>
>> Why not make a trait BaseString and apply all the operations to it?
>
>
> It's "heavier", in several ways.
>
> For the client: the Slice is written once, with all its operations, so where
> in an "interface" setting you would need to provide a lot of operations
> yourself, here you just provide ONE operation => the conversion so slice.
>
> For the user: a Slice is a concrete type, all functions/methods are thus
> statically known and can be inlined at call site. The alternative with a
> trait is either:
> - paying a virtual call at each method, which is slower and inhibit constant
> propagation a whole host of potential optimizations
> - mono-morphizing the code for each implementer of BaseString, which is
> generally referred to a "template bloat" in C++ (much more code is
> generated, thus bigger binaries, clogging of the CPU caches, ...)
>
> In short, Slice is just a superior alternative.
>
>
>
>>
>>
>> On Thu, May 29, 2014 at 3:44 AM, Matthieu Monrocq
>> <[email protected]> wrote:
>> >
>> >
>> >
>> > On Thu, May 29, 2014 at 2:38 AM, Oleg Eterevsky <[email protected]>
>> > wrote:
>> >>
>> >> Hi!
>> >>
>> >> I've just recently started learning Rust. Here's a few questions that
>> >> I have after writing about a hundred lines of code:
>> >>
>> >> 1. Why neither overloaded function, nor default values for arguments
>> >> are supported? In statically typed language overloaded functions are
>> >> quite safe and convenient. Also, from what I see, overloaded functions
>> >> could be used in various use cases instead of macros.
>> >>
>> >
>> > Overloaded functions are unnecessary in Rust. In C++ they are heavily
>> > used
>> > because of templates use duck-typing, but Rust uses explicit interfaces
>> > (typeclass-like) which makes it unnecessary.
>> >
>> > Overloaded functions also don't play too well with type inference. C++
>> > works
>> > around this by having a very simple type inference (ie, it only deduces
>> > the
>> > return type), but Rust's type inference is more advanced:
>> >
>> >     let mut result = vec!(); // type of "result" is "Vec<...>" which is
>> > only
>> > partially known
>> >
>> >     for x in some_range.iter() {
>> >         doit(result, *x);    // there is a single "doit" in scope, whose
>> > arguments is "Vec<int>" thus this is the type of "result"
>> >     }
>> >
>> > If you were able to overload "doit" though, suddenly things would get
>> > hairy,
>> > and you would have to start explicitly typing your variables left and
>> > right.
>> >
>> > Thus, even besides the confusion a human being experiences (which
>> > overload
>> > is called ?) and the over-abuse of overloads in the first place (Qt
>> > seems to
>> > be doing nicely in that domain), there is a technical trade-off: better
>> > type
>> > inference or function overload ?
>> >
>> > Note: it also means that compiler error-messages are generally more
>> > explicit; tried forgetting to implementation "operator<<" for a type and
>> > see
>> > what your favorite C++ compiler reports ;)
>> >
>> >
>> > Named parameters and default values are just syntactic sugar; they might
>> > feel handy, indeed, but in general they can be emulated => Rust already
>> > has
>> > named parameters for "struct" initialization, and it also has functional
>> > update for those "struct" thus as long as you are willing to add one
>> > more
>> > symbol:
>> >
>> >     struct FooArgs { x: int, y: int, }
>> >
>> >     static DEFAULT_FOO: FooArgs = FooArgs { x: 4, y: 5, }
>> >
>> >     fn foo(args: FooArgs) {}
>> >
>> >     fn main() {
>> >         foo(FooArgs{x: 3, .. DEFAULT_FOO})
>> >     }
>> >
>> > I personally feel that the name "FooArgs" could be inferred in the call
>> > (since there is no overload of "foo" there is no ambiguity) which would
>> > assist in this pattern. There is also the issue that static cannot be
>> > build
>> > from arbitrary functions, but that may be lifted with Compilation Time
>> > Function Evaluation one day, and in the mean time you can have a
>> > FooArgs::default() that computes the defaults.
>> >
>> >
>> >>
>> >> 2. What about exceptions? Is it a design decision not to support them,
>> >> or are they planned for some future version? I understand, that
>> >> exceptions make memory management more difficult, but maybe there are
>> >> ways to restrict their usage to avoid problems (like making it
>> >> impossible to pass references as exception parameters?)
>> >>
>> > As mentioned, the issue is about recovery: you modified a lot of stuff
>> > in
>> > your "try" that is still accessible afterward, is it in a safe state ?
>> > is it
>> > in sensible state ? No one knows, and your compiler does not care. Tasks
>> > enforce explicitness about what is modified, which helps in auditing.
>> >
>> >>
>> >> 3. It seems like almost any string operation requires as_slice().
>> >> Can't various string methods be also implemented for String?
>> >>
>> > It's the lowest common denominator approach, a slice is cheap (no memory
>> > allocation) and universal (may refer either to heap allocated buffer or
>> > static buffer).
>> >
>> > Rust is working on DST (Dynamically Sized Types) and once this lands
>> > String
>> > will be able to implement Deref, which will ease things. There is also
>> > been
>> > talk about auto-deref to make it even painless.
>> >
>> >>
>> >> 4. It looks like vectors can be concatenated with + operations, but
>> >> strings can't. Is it deliberate?
>> >
>> >
>> > As someone else answered, it could be done.
>> >
>> > Personally though I've never liked that. '+' is mathematically a
>> > commutative
>> > operation and catenation is not a commutative operation. Also, in the
>> > absence of overloading this means to catenate you have to implement the
>> > Add
>> > trait. This is just weird. I would prefer infix function calls (`cat`)
>> > for
>> > example, or another dedicated operator (++ ?)
>> >
>> >>
>> >>
>> >> 5. Simple indexing doesn't work for vectors:
>> >>   let a = vec![1, 2, 3];
>> >>   println!("{}", a[0]);
>> >> It's a bit surprising...
>> >>
>> > Could probably be implemented, may have to wait for DST though.
>> >
>> >>
>> >> 6. impl ToStr for custom struct fails:
>> >>   error: conflicting implementations for trait `std::to_str::ToStr`
>> >>   note: conflicting implementation in crate `std`
>> >> Is it a bug? Is Show implicitly assumed for all struct's?
>> >>
>> >> 7. The usage of mut is a bit confusing. It is supposed to be used as a
>> >> qualifier for a variable, but it quickly becomes a part of the type,
>> >> when you define functions like
>> >>   fn test(something: &mut Something)
>> >> Maybe it makes sense move mut their? Or distinguish mut as a variable
>> >> qualifier vs const as a propery of type?
>> >>
>> >
>> > There has been an extended discussion about it, and I am also slightly
>> > confused by the use of `mut`. It reads "mutability" but in the case of
>> > references also implies exclusivity...
>> >
>> > At the end of the day though, there's already a number of high-profiles
>> > changes going on (such as the above DST), so for now it's wait and see.
>> > Once
>> > those are implemented and the core developers have a bit more time on
>> > their
>> > plate, the issue might be revisited.
>> >
>> >>
>> >> --
>> >> Thanks,
>> >> Oleg Eterevsky.
>> >> _______________________________________________
>> >> Rust-dev mailing list
>> >> [email protected]
>> >> https://mail.mozilla.org/listinfo/rust-dev
>> >
>> >
>
>
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to