Hacker News new | ask | show | jobs
by jcelerier 1790 days ago
Which of "Rust, Haskell (core Haskell), OCaml, and SML" is able to parametrize types on values, à la template<auto> ?
3 comments

It only supports integers so far, which c++ could do pretty much since before it was even standardized 25 years ago. Doesn't seem to support as wide of a type menagerie as C++20's NTTP. Does it even support parametrization over function pointers ?
> It only supports integers so far

Integer-like things. C++ char is similar to Rust's u8 or i8 but Rust's char and bool are very deliberately not just integers.

> Does it even support parametrization over function pointers ?

Can you give a clear example ? I think the answer is "No" but I struggled to put together an application for the feature I'm imagining.

You would like to define a type, which is parameterised not by the type of function, but by specific functions? So, for example I can make a foo<sum> and a foo<average> and those are distinct types which presumably internally are using the provided function to behave differently?

Except all the examples I think of come out better with just parametrisation over traits instead. So a clear example from you might illuminate whether this is a sizeable hole or just a difference of philosophy.

> Integer-like things. C++ char is similar to Rust's u8 or i8 but Rust's char and bool are very deliberately not just integers.

sure, but there's an easy mapping from char / bool to integers. C++ supports parametrizing on values of struct type, which is a clear increase in expressive power:

    struct foo {
      int count;
      float init;
    };
    
    template<auto arg> struct bar {
      float x[arg.count];
      
      bar() {
       for(int i = 0; i < arg.count; i++) 
         x[i] = arg.init;
      }
    };
    
    constexpr foo f{ .count = 123, .init = 4.56 };
    bar<f> b;


> You would like to define a type, which is parameterised not by the type of function, but by specific functions? So, for example I can make a foo<sum> and a foo<average> and those are distinct types which presumably internally are using the provided function to behave differently?

yes, it's pretty much the only way in C++ to have zero-cost callbacks / strategy pattern using function pointers since the compiler can directly inline the function pointer everywhere in the type's implementation instead of having to go and read it from some variable at run-time

You're correct to observe that more sophisticated value parametrisation might be useful. At least &str (imagine roughly a nicer std::string_view as a built-in type) is on the horizon for Rust.

Because Rust cares a lot about soundness, this gets very tricky for user-defined types. The current idea is that they could be allowed if they derive Eq. What "derive Eq" means in practice is that the compiler is comfortable believing that either you can compare instances of these types to one another with a bit-match or they're composed of types with that property.

You wouldn't be allowed to implement Eq instead for this, because such implementations are safe in Rust, and thus their promises are worthless (Rust has unsafe traits like Allocator, and if you choose to implement those and get it wrong your program has Undefined Behaviour, but by definition that won't happen for a safe trait, even if you deliberately implement it contrary to the specification)

So that rules out floating point numbers because you shouldn't try to compare those to each other with bit matching - NaNs for example.

What does C++ do here? Just YOLO, if you make poor choices the resulting code is nonsense and too bad?

--

We still don't have an example, the constant function pointer thing feels unergonomic to me, I think I'd cook up a Trait representing whatever it is that these functions have in common, and then implement that Trait as necessary to get the same effect by parametrising on the Trait -- but I may very well be missing some affordance your preferred approach has since the equivalent C++ is just to use a Class and finalize the implementations yet you aren't doing that.

In extremely limited form. That should change in the future, though.
Much more usefully, in C++ you can also parametrize over templates, which I think you still can't in rust.

You can't parametrize over namespaces (at least not directly) which is an annoying and arbitrary restriction.

> You can't parametrize over namespaces (at least not directly) which is an annoying and arbitrary restriction.

It's not a restriction, namespaces are neither types nor values so they would need specific support. Given that you can (ab)use classes with static members as namespaces which are also a type it's simply that nobody cared enough to add support for templating over real namespaces.

You are right it is not a restriction in the sense that is explicitly disallowed; what I meant is that it would take very little to add support for parametrizing over namespaces.

Stateless structures are a workaround (so is adl driven by a template parameter), still direct support would be nice.

For those languages, I don't really see a need to paremtrize types on values. Because of the AMAZING generics support.

But that may just be the blub paradox [1] in action

[1] https://wiki.c2.com/?BlubParadox

Well the fact you couldn't reasonably use arrays without Generic Const hit stable in Rust 1.51 says otherwise.

Defining templates on values is very useful, especially in C++ where you can provide template specializations. You can do a whole lot of metaprogramming and compile-time stuff that way.