Live data from Hacker News

Codata in action, or how to connect FP and OOP

javiercasas.com

11–20 of 58 posts

Re: Codata in action, or how to connect FP and OOP

#11

Earlier quoted context omitted.

Any OOP with inheritance does multiple dispatch. Java, C++ vitual functions, python via ducktyping. The article's concern is real but overstated in my opinion. When adding a new function in a hierarchy you'll either have a good default or you'll be implementing via type matching branches or 10 implementations in each type behind multiple dispatch. The difference, to me, is minimal.

> Any OOP with inheritance does multiple dispatch. Java, C++ vitual functions, python via ducktyping. Java, Python and C++ require Visitor machinery that you ahev to write, for each case, to implement multiple dispatch (and Visitor only works for double dispatch, if you want more than 2 arguments you need even more boilerplate). Julia and CLOS essentially have that machinery built in. In case this is not clear, multi…

> Java, Python and C++ require Visitor machinery that you ahev to write, for each case, to implement multiple dispatch (and Visitor only works for double dispatch, if you want more than 2 arguments you need even more boilerplate). Julia and CLOS essentially have that machinery built in.

we're in 2020

     using value = std::variant;
     void foo(value a, value b, value c) {
       struct {
          void operator(int, float, string) { }
          void operator(float, int, string) { }
          void operator(float, string, int) { }
          ///... etc... 
       } visitor;

       std::visit(visitor, a, b, c);
     }
works fine (and has worked fine since a long time with either boost.variant or in C++11 with eggs.variant, etc...)

Re: Codata in action, or how to connect FP and OOP

#12

" (...) - In an OO language, you can add a new case by creating another subclass and implementing the methods. But, if you want to add a new function to a datatype, you have to add it to the superclass and implement it on all the existing subclasses, which means modifying a lot of files and recompiling most of the program. - In a FP language you can add a new function to an existing datatype just by adding a function…

Solving the expression problem requires maintaining static type safety. AFAIK Julia doesn’t do this because it’s dynamically typed.

Re: Codata in action, or how to connect FP and OOP

#14

" (...) - In an OO language, you can add a new case by creating another subclass and implementing the methods. But, if you want to add a new function to a datatype, you have to add it to the superclass and implement it on all the existing subclasses, which means modifying a lot of files and recompiling most of the program. - In a FP language you can add a new function to an existing datatype just by adding a function…

I don't think multiple dispatch fixes this problem in any way. The idea here is that you can have an opaque interface. Then, adding new implementations of that interface is very easy. But, if you want to extend the interface, you need to modify all of the existing implementations. Multiple dispatch suffers of this problem just as much as single dispatch. In the other case, you have a transparent interface - anyone ca…

> I'm not sure that this still allows subtyping of that GADT as if it were a regular class.

It does, more or less, since any subtype will have to implement the "accept" method for the visitor, which has been defined to only 'accept' the values that can be represented by the GADT. Any subtype will need to define how its possible values map to that of the supertype.

Re: Codata in action, or how to connect FP and OOP

#15

" (...) - In an OO language, you can add a new case by creating another subclass and implementing the methods. But, if you want to add a new function to a datatype, you have to add it to the superclass and implement it on all the existing subclasses, which means modifying a lot of files and recompiling most of the program. - In a FP language you can add a new function to an existing datatype just by adding a function…

I don't think multiple dispatch fixes this problem in any way. The idea here is that you can have an opaque interface. Then, adding new implementations of that interface is very easy. But, if you want to extend the interface, you need to modify all of the existing implementations. Multiple dispatch suffers of this problem just as much as single dispatch. In the other case, you have a transparent interface - anyone ca…

Some Rust libraries use a trick to make sum types extensible without introducing breaking changes when a new invariant is added. There is a private/undocumented invariant in the type so every pattern match over the type must have a default/catch-all/wildcard branch (e.g. it is not possible to do an exhaustive match by naming all existing variants). By having a default branch, the match is always exhaustive and there will be no error if a new variant is added. For fields in a sum type something similar can be done: an undocumented, hidden field that cannot be used when the variant is destructed. Therefore it can be only partially destructed and in this case it doesn't matter how many other fields there are in the type variant.

Here is an example: https://github.com/rust-lang/regex/blob/master/regex-syntax/...

Re: Codata in action, or how to connect FP and OOP

#16
post #12

" (...) - In an OO language, you can add a new case by creating another subclass and implementing the methods. But, if you want to add a new function to a datatype, you have to add it to the superclass and implement it on all the existing subclasses, which means modifying a lot of files and recompiling most of the program. - In a FP language you can add a new function to an existing datatype just by adding a function…

Solving the expression problem requires maintaining static type safety. AFAIK Julia doesn’t do this because it’s dynamically typed.

People using Julia are quite happy how it alleviates the expression problem compared to languages they used before. Your argument, if I get it right, that Julia can't solve the expression problem because "the expression problem requires maintaining static type safety" reminds of the "Bumblebees can't fly" fable.

Re: Codata in action, or how to connect FP and OOP

#17
While we're on this subject, if we were to keep extending the syntax, is this what introProduct would have to look like?

        data Coproduct a b where
            InjL :: a -> Coproduct a b
            InjR :: b -> Coproduct a b

        elimCoproduct :: (a -> c) -> (b -> c) -> Coproduct a b -> c
        elimCoproduct f g (InjL x) = f x
        elimCoproduct f g (InjR y) = g y

        codata Product a b where
            ProjL :: Product a b -> a
            ProjR :: Product a b -> b

        introProduct :: (c -> a) -> (c -> b) -> c -> Product a b
        ProjL (introProduct f g z) = f z
        ProjR (introProduct f g z) = g z

Re: Codata in action, or how to connect FP and OOP

#18
post #16
post #12

Earlier quoted context omitted.

Solving the expression problem requires maintaining static type safety. AFAIK Julia doesn’t do this because it’s dynamically typed.

People using Julia are quite happy how it alleviates the expression problem compared to languages they used before. Your argument, if I get it right, that Julia can't solve the expression problem because "the expression problem requires maintaining static type safety" reminds of the "Bumblebees can't fly" fable.

Most people using language X are quite happy how X does things compared to languages they used before. That's why they use the language X. That's also the reason why I consider this type of argument unhelpful.

Re: Codata in action, or how to connect FP and OOP

#19
post #16
post #12

Earlier quoted context omitted.

Solving the expression problem requires maintaining static type safety. AFAIK Julia doesn’t do this because it’s dynamically typed.

People using Julia are quite happy how it alleviates the expression problem compared to languages they used before. Your argument, if I get it right, that Julia can't solve the expression problem because "the expression problem requires maintaining static type safety" reminds of the "Bumblebees can't fly" fable.

Well, the expression problem is a clearly formulated question: how can we have three desirable things at the same time (no need to modify existing code, no need for code repetition and no need for runtime type errors [1])? See also the project management triangle: if you need something fast, good and cheap, then fast, good and expensive won't be a satisfactory answer, no matter how many millionaires are satisfied with the latter.

[1] https://link.springer.com/chapter/10.1007/978-3-540-24851-4_...

Re: Codata in action, or how to connect FP and OOP

#20
post #18
post #16

Earlier quoted context omitted.

People using Julia are quite happy how it alleviates the expression problem compared to languages they used before. Your argument, if I get it right, that Julia can't solve the expression problem because "the expression problem requires maintaining static type safety" reminds of the "Bumblebees can't fly" fable.

Most people using language X are quite happy how X does things compared to languages they used before. That's why they use the language X. That's also the reason why I consider this type of argument unhelpful.

No, not "most people" and not "happy how X does things" but specifically people who have thought about the expression problem's opinion about how Julia alleviates the expression problem compared to other languages they know.
Post reply on HN