On Mon, Apr 21, 2008 at 10:58 PM, Jonathan Cast <jonathanccast@fastmail.fm> wrote:
I must have failed to communicate well. To me, the point of giving a class a name is that then you can write a program that is parametric over the elements of that class. Knowing that I can implement monads in Ruby doesn't impress me nearly as much as knowing that I can implement mapM does. Haskell has me addicted to code reuse (mapM) the way the rest of the programming world is addicted to design patterns (monads). What I mean by `encoding Num and Monad' is that you can do something like this:
sum = foldr (+) 0 sequence = foldr (liftM2 (:)) (return [])
I don't know of any language that is dynamically typed and also can encode `return' or `0' in a way that lets those examples work. Statically typed languages where it works are rare, even. Haskell gives up a fair amount of control by making these things implicit, which is what I think you're running up against --- but I think it gets something useful and non-trivial to acheive in return.
I think ruby generally solves this problem via duck-typing; instead of the cast happening in the (implicit) fromInteger call in sum above, instead the cast happens in the function with more information via a call to otherclass#to_whatever_i_am. You can do something like this: class Frob attr_reader :val def initialize(i) @val = i end def to_frob self end def +(rhs) rhsF = rhs.to_frob Frob.new(rhsF.val + @val) end end class Integer def to_frob Frob.new(self) end end class Array def sum foldl(0) {|acc,x| acc + x} end def foldl(z) each {|x| z = yield(z,x)} z end end irb(main):055:0> [1,2,3].sum => 6 irb(main):057:0> [1,2,3].map {|x| Frob.new(x)}.sum => #<Frob:0x2b65cf0 @val=6>
I'll agree with this point. I've complained, concretely, about the lack of instances for (some) standard types before. (STM is actually a rather bad offender here; it's lacking MonadPlus, as well, despite the specific observation in the paper that it has the right signature for that class.)
Actually: GHCi, version 6.8.2: http://www.haskell.org/ghc/ :? for help Loading package base ... linking ... done. Prelude> :m Control.Monad Control.Monad.STM Prelude Control.Monad.STM Control.Monad> :i STM ... instance MonadPlus STM -- Defined in Control.Monad.STM
When can we discharge a MaybeInstance context?
On any concrete type. Like Typeable should be :) The compiler then determines whether that type is an instance of the class and provides the appropriate dictionary if applicable.
Having the | Show a test suddenly trip from False to True because some other module imported Text.Show.Functions sounds like an odd change to me. At any rate, it scares me enough to make me oppose the idea.
I see the worry now. I think this is more of a problem with orphan instances; are orphan instances considered to be a valuable enough feature to avoid potentially more powerful constructs? Maybe there is a better solution to the "I have class C from library X and type T from library Y and I want to make them play nice together" problem than orphan instances.
class Forceable alpha where seq :: alpha -> beta -> beta
Instances derived automatically by the compiler, when possible, for every type (like Typeable should be). We can omit functions if desired (I don't remember why I thought this was a good idea). When you say
f :: alpha -> beta
or
f :: C alpha => alpha -> beta
The compiler adds implicit Forceable constraints on alpha and beta. But, if you say
f :: !Forceable alpha => alpha -> beta
The compiler leaves the Forceable alpha constraint off. Then you can say
build :: (forall c. !Forceable c => (a -> c -> c) -> c -> c) -> [a]
And the foldr/build law is still sound.
This seems like a really interesting idea. -- ryan