Is it possible to get the effect of the following:
overload 123 = True
overload "Test" = False
...
Hugs> overload 123 -- Prints "True"
Hugs> overload "Test" -- Prints "False"
The error I get for the above function definition is "Instance of Num
[Char] required for definition of overload".
Am I "barking up the wrong tree" (i.e. asking for something
unreasonable), or might it be possible to define such a function some
way, perhaps using type classes and/or algebraic databtypes? I've read
various dokumentation on these, as well as done some experimentation,
but haven't found a way.
Related to this, the following doesn't type-check:
class TestClass a where
test :: a -> a -> Bool
instance TestClass Int where
test a b = True
Hugs> test 1 2 -- Expecting "True"
ERROR - Unresolved overloading
*** Type : (Num a, TestClass a) => Bool
*** Expression : test 1 2
Does anyone know why, and how to fix it?
Hm, that turned out to be quite easy, using plain "data":
data TestType = IntValue Int | StringValue String
overload :: TestType -> Bool
overload (IntValue 123) = True
overload (StringValue "Test") = False
Hugs> overload (IntValue 123) -- Prints "True"
Hugs> overload (StringValue "Test") -- Prints "False"
I'm still wondering about the last question in the posting, though.
> Am I "barking up the wrong tree" (i.e. asking for something
> unreasonable), or might it be possible to define such a function some
> way, perhaps using type classes and/or algebraic databtypes? I've read
> various dokumentation on these, as well as done some experimentation,
> but haven't found a way.
Since "Test" and 123 are of different types, you cannot use one single
function and pattern matching. You must use a type class. You're already
writing a good type class below.
> instance TestClass Int where
> test a b = True
>
> Hugs> test 1 2 -- Expecting "True"
>
> ERROR - Unresolved overloading
> *** Type : (Num a, TestClass a) => Bool
> *** Expression : test 1 2
>
> Does anyone know why, and how to fix it?
Numbers are overloaded using type classes too. Things like 1 and 2 are
not necessarily Int; they are under the type class Num. Several
instances are Int, Integer, Float, Double, and Complex Double. So "test
1 2" is ambiguous. You should use something like "test (1 :: Int) 2" to
disambiguate.
> overload 123 = True
> overload "Test" = False
The data solution you found is nice if you don't mind the extra
constructors. That kind of pattern is used a lot. I like it, anyway.
> Related to this, the following doesn't type-check:
You also seem to be finding your way to a class-based solution:
> class TestClass a where
> test :: a -> a -> Bool
>
> instance TestClass Int where
> test a b = True
>
> Hugs> test 1 2 -- Expecting "True"
>
> ERROR - Unresolved overloading
> *** Type : (Num a, TestClass a) => Bool
> *** Expression : test 1 2
>
> Does anyone know why, and how to fix it?
Does test (1::Int) (2::Int) do any better? I wonder if the system isn't
sure what your numeric literals are.
Sometimes I find myself having to use GHC's TypeSynonymInstances
extension but I'm not sure why - I should look into that and figure it
out.
An early difficulty I had when first designing Haskell programmes was
figuring out when to use classes.
Mark
Yes, I also figured that the type inferencing got confused, and
specifying the type of one of the arguments solved it, thanks.
Yes. :) However, I found an ADT-based solution first. I'm also going
to look into a class-based solution to this.
It's unfortunate that "algebraic datatype" and "abstract datatype" has
the same acronym, but I guess it's often possible to understand which
is meant, from the context (here I meant the first).
> > class TestClass a where
> > test :: a -> a -> Bool
>
> > instance TestClass Int where
> > test a b = True
>
> > Hugs> test 1 2 -- Expecting "True"
>
> > ERROR - Unresolved overloading
> > *** Type : (Num a, TestClass a) => Bool
> > *** Expression : test 1 2
>
> > Does anyone know why, and how to fix it?
>
> Does test (1::Int) (2::Int) do any better? I wonder if the system isn't
> sure what your numeric literals are.
Yes, that's right (as the other poster said, it's no wonder as they
are members of several type classes), and it was actually necessary to
just specify the type of one argument, to get it to work.
> Sometimes I find myself having to use GHC's TypeSynonymInstances
> extension but I'm not sure why - I should look into that and figure it
> out.
I'll look into this, too.
> An early difficulty I had when first designing Haskell programmes was
> figuring out when to use classes.
Right. Not to mention figuring out what they _are_. :) Often, they are
compared to classes in languages like C++ and Java, but I think that
comparison is wrong, or at least unhelpful. When you instantiate type
classes you get a class (type), whereas when instantiating a C++
class, you get an object (value). One can see the relationship like
this:
Haskell: Type class -> type -> value
C++: X -> type (class) -> value (object)
At the moment there isn't something corresponing to X, above (although
there is, to some extent, in Java and C#, in the form of constrained
generics, which will also come in C++, as shown below). However, in
the coming new version of the C++ standard (usually referred to as C+
+0x, which will likely end us as C++09, i.e. the standard being
completed in 2009), there will be a facility similar to type classes
in Haskell, called "concepts".
For those that may be interested, here's a comparison of how you might
do it in each language:
Haskell:
-- Define type type class Eq
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
-- Declare that MyClass is an instance of Eq
instance Eq MyClass where
a == b = a `myEqual` b
-- Use type class Eq
test :: Eq a => a -> a -> Bool
test a b = a == b
C++:
// Define concept EqualityComparable
concept EqualityComparable<typename T>
{
bool operator==(T,T);
bool operator!=(T,T);
}
// Declare that MyClass conforms to EqualityComparable
concept_map EqualityComparable<MyClass> {}
// Use concept EqualityComparable
template<EqualityComparable T>
bool test(T a,T b)
{
return a == b;
}
As you can see, there's a close correspondence, and like Haskell (and
unlike the mentioned class/object comparison) the concept checking is
done at compile time.
Also, if you define the concept as "auto concept ...", you don't need
to explicitly declare that MyClass conforms to (is an instance of, in
Haskell terminology) the EqualityComparable concept; it will be
inferred from its interface (similar to "duck typing").
In Haskell, it's possible for a type class to "inherit" from other
type classes, and the same facility is available in the C++ facility,
called "concept refinement" there.
(Reference to C++ concepts: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2081.pdf)
By the way, C++0x will also be getting lambda functions. :)