Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This is definitely true, but you can often get surprisingly far by just boxing, cloning, Rc-ing, etc. whenever you hit something like this.


the bigger issue is around the combination of closures, generic parameters, and traits. because higher-rank types can't be expressed and closures are all `impl Fn()`, you start to get an explosion in the complexity of managing the types and implementing the trait. try implementing something in the "finally tagless" style for an example of the kinds of things that can go wrong really fast. `Box` and `Rc` don't get you out of it and the resulting implementation is brittle and boiler-plate heavy in actual use.

from memory:

    trait Prop<'a, T> {
        fn p<F: Fn(&'a T) -> bool>(f: F) -> Self;
        fn eval(&self, &'a T) -> bool;
    }

    enum LiftedBool<'a, T, F: Fn(&'a T) -> bool> {
        Base(Box<F>),
        And(LiftedBool<'a, T, F>, LiftedBool<'a, T, F>),
        Or(LiftedBool<'a, T, F>, LiftedBool<'a, T, F>),
        Not(LiftedBool<'a, T, F>),
    }

    impl<'a, T, F: Fn(&'a T) -> bool> Prop<'a, T> for LiftedBool<'a, T, F> {
        fn p<F: Fn(&'a T) -> bool>(f: F) -> Self {
            // this fails because `F` in the impl doesn't necessarily unify with `F` in the trait definition
            // it can be made to work with `dyn` for the function argument but requires boilerplate to actually use
        }

        fn eval(&self, &'a T) -> bool {
            // obvious evaluation by cases
        }
    }
this example can be fixed to work via `dyn` for the argument on the trait function and the struct itself but it becomes increasingly painful to actually work with. this kind of generic work is not at all ergonomic but very easy in Haskell.


Yes, but doesn't the additional housekeeping negate much of the elegance of Haskell?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: