I want to build a list of different things which have one property in common, namely, they could be turned into string. The object-oriented approach is straightforward: define interface Showable
and make classes of interest implement it. Second point can in principle be a problem when you can't alter the classes, but let's pretend this is not the case. Then you create a list of Showable
s and fill it with objects of these classes without any additional noise (e.g. upcasting is usually done implicitly). Proof of concept in Java is given here.
My question is what options for this do I have in Haskell? Below I discuss approaches that I've tried and which don't really satisfy me.
Approach 1: existensials. Works but ugly.
{-# LANGUAGE ExistentialQuantification #-} data Showable = forall a. Show a => Sh a aList :: [Showable] aList = [Sh (1 :: Int), Sh "abc"]
The main drawback for me here is the necessity for Sh
when filling the list. This closely resembles upcast operations which are implicitly done in OO-languages.
More generally, the dummy wrapper Showable
for the thing which is already in the language — Show
type class — adds extra noise in my code. No good.
Approach 2: impredicatives. Desired but does not work.
The most straightforward type for such a list for me and what I really desire would be:
{-# LANGUAGE ImpredicativeTypes #-} aList :: [forall a. Show a => a] aList = [(1 :: Int), "abc"]
Besides that (as I heard)ImpredicativeTypes
is “fragile at best and broken at worst” it does not compile:
Couldn't match expected type ‘a’ with actual type ‘Int’ ‘a’ is a rigid type variable bound by a type expected by the context: Show a => a
and the same error for "abc"
. (Note type signature for 1: without it I receive even more weird message: Could not deduce (Num a) arising from the literal ‘1’
).
Approach 3: Rank-N types together with some sort of functional lists (difference lists?).
Instead of problematic ImpredicativeTypes
one would probably prefer more stable and wide-accepted RankNTypes
. This basically means: move desired forall a. Show a => a
out of type constructor (i.e. []
) to plain function types. Consequently we need some representation of lists as plain functions. As I barely heard there are such representations. The one I heard of is difference lists. But in Dlist
package the main type is good old data
so we return to impredicatives. I didn't investigate this line any further as I suspect that it could yield more verbose code than in approach 1. But if you think it won't, please give me an example.
Bottom line: how would you attack such a task in Haskell? Could you give more succinct solution than in OO-language (especially in place of filling a list — see comment for code in approach 1)? Can you comment on how relevant are the approaches listed above?
UPD (based on first comments): the question is of course simplified for the purpose of readability. The real problem is more about how to store things which share the same type class, i.e. can be processed later on in a number of ways (Show
has only one method, but other classes can have more than one). This factors out solutions which suggest apply show
method right when filling a list.
If you are using an interactive Haskell prompt (like GHCi) you can type :t <expression> and that will give you the type of an expression. e.g. or e.g.
Haskell isn't an object-oriented language. All of the functionality built here from scratch already exists in a much more powerful form, using Haskell's type system.
Haskell classes are roughly similar to a Java interface. Like an interface declaration, a Haskell class declaration defines a protocol for using an object rather than defining an object itself.
Does Haskell have inheritance? Well, no, it doesn't, because Haskell does not have objects, and inheritance is a relationship between two objects. Objects are a combination of internal state (data) and methods (behavior).
Since evaluation is lazy in Haskell, how about just creating a list of the actual strings?
showables = [ show 1, show "blah", show 3.14 ]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With