3 ms·
> A function of type fn<T>(T) -> T must: > return its argument or > panic or abort or > never return This confused me for a moment because I thought “why? i
by bringtheaction 9y ago
> A function of type fn<T>(T) -> T must:
> return its argument or
> panic or abort or
> never return
This confused me for a moment because I thought “why? it only says the return value must be the same type as the argument, not that it must have the same value”, but of course since the type can be anything there is no operation that is guaranteed to work that you can perform on the argument value. You can’t for example multiply by two because not all types will support multiplication by an integer.
- solidsnack9000 9y agoThe linked paper by Wadler (https://people.mpi-sws.org/~dreyer/tor/papers/wadler.pdf https://people.mpi-sws.org/~dreyer/tor/papers/wadler.pdf) develops many more examples like this (e.g. "Figure 1: Examples of theorems from types").
- deleted 9y ago[deleted]
- kccqzy 9y agoExactly. Welcome to the world of parametric polymorphism. Writing programs using those uninterpreted types are essentially proofs for logic by the Curry-Howard correspondence. It’s a fascinating topic.
- hhmc 9y agoPerhaps this is subverting the type-theoretic (in a CS sense) point being made, but in C++ you could quite reasonably write a function: template<typename T> T fn(T t){ return t * 2; } fn(4.2); //fine fn("foo"); //fails because the inability to multiply by 2 isn't a problem until an incompatible type parameter is instantiated. Is this not the case in rust?
- K0nserv 9y agoNope in Rust you can only do things with generic types that fit the constraints(called trait bounds in Rust) you've placed on them. So with no constraints you can only do things that every single type supports. In essence it's an implicit vs explicit trade off. C++ implicitly figures out the constraints on generic types whereas Rust requires you to be explicit about them. Your example in Rust would be use std::ops::Mul; fn double<T: Mul<i32>>(a: T) -> T::Output { a * 2 } fn main() { println!("{}", double(2)); // Can't double &str because it doens't implement Mul<i32> // println!("{}", double("Hello")); } https://play.rust-lang.org/?gist=ba8d6368a7fb997586b51580baa75504&version=stable https://play.rust-lang.org/?gist=ba8d6368a7fb997586b51580baa... Also as the GP points out the above fact is crucial for the reasoning around to hold `fn<T>(T) -> T`. An example that breaks those rules is fn thing<T: Default>(a: T) -> T { T::default() }
- beojan 9y agoThat's because templates have no type-checking in C++. Ideally, you wouldn't be able to do this with concepts, but I don't know whether that's true for the concepts that finally made it into the standard (i.e. I don't know if it would be an error to call a function or operator on an argument if that function isn't required in the concept specification.).
- deleted 9y ago[deleted]
- seanmcdirmid 9y agoC++ templates have type checking after expansion, the type checking just isn’t modular.