I'm new to Purescript. My current learning exercise is to create a tagged union of polymorphic Array and List. I'll use it in a function that finds the length of any Array or List. Here's my attempt:
import Data.List as L
import Data.Array as A
data Collection = CollectionList (forall a. L.List a)
| CollectionArray (forall b. Array b)
colLength :: Collection -> Int
colLength (CollectionList list) = L.length list
colLength (CollectionArray arr) = A.length arr
main :: Effect Unit
main = do
logShow (colLength (CollectionArray [3,5]))
The compiler doesn't like it:
Could not match type Int with type b0
while checking that type Int is at least as general as type b0
while checking that expression 3 has type b0
in value declaration main
where b0 is a rigid type variable
I'm confused by the parts, checking that type Int is at least as general as type b0
and b0 is a rigid type variable
. My intention was to allow b
to be anything. Not sure what I did to make the compiler put conditions on what b
can be.
If you know how, please show the correct way to define a tagged union of polymoric types that'll work in my colLength
function.
forall a
doesn't mean "any type goes here"
It means that whoever accesses the value, gets to choose what a
is, and whoever provides the value has to make sure that the value is of that type. It's a contract between the provider and the consumer.
So when you provide the value CollectionArray [3,5]
, you have to make it such that it works for all possible a
that whoever accesses that value later might choose.
Obviously, there is only one way you can construct such value:
CollectionArray []
What you probably actually meant to do (and I'm guessing here) was to make your collection polymorphic, in the sense that it can contain values of any type, but the type is chosen by whoever creates the collection, and then whoever accesses it later has to deal with that particular type.
To do that, you have to put the type variable on the outside:
data Collection a = CollectionList (L.List a)
| CollectionArray (Array a)
That way, when you create a collection CollectionArray [3,5]
, it becomes of type Collection Int
, and now everywhere you pass it, such as colLength
, will have to deal with that Int
This, in turn, can be achieved by making colLength
itself generic:
colLength :: forall a. Collection a -> Int
colLength (CollectionList list) = L.length list
colLength (CollectionArray arr) = A.length arr
Now whoever accesses (i.e. calls) colLength
itself gets to choose what a
is, which works fine, because it's the same place that created the Connection Int
in the first place.