Re: is identity the only polymorphic function without typeclasses?

Cagdas Ozgenc
Greetings,
Is identity function the only meaningful function one can write without constraining the type variable using a typeclass? If not, could you please give a counter-example?
Certainly you can write lots of ``meaningful function''s without type classes: not, (&&), (||), as well as many more complicated functions at more complicated types. You can also write useful polymorphic functions without type classes, as long as you specify at least one type. For example, you can write polymorphic functions over/yielding lists, such as repeat, cycle, map and its many relatives, foldr and its many relatives, take and its relatives, takeWhile and its relatives, etc. Similar functions often exist for other types. I'm somewhat curious, though: why do you ask this question? How do you expand your question that makes the answer seem to be ``no''?
Thanks
Jon Cast

Cagdas Ozgenc
wrote: Greetings,
Is identity function the only meaningful function one can write without constraining the type variable using a typeclass? If not, could you please give a counter-example?
Certainly you can write lots of ``meaningful function''s without type classes: not, (&&), (||), as well as many more complicated functions at more complicated types.
You can also write useful polymorphic functions without type classes, as long as you specify at least one type. For example, you can write polymorphic functions over/yielding lists, such as repeat, cycle, map and its many relatives, foldr and its many relatives, take and its relatives, takeWhile and its relatives, etc. Similar functions often exist for other types.
I'm somewhat curious, though: why do you ask this question? How do you expand your question that makes the answer seem to be ``no''?
I did not mean to include functions that take type constructors as parameters (so lists are out of my discussion scope). I am only considering functions that uses type variables that are not restricted by typeclasses. In this setting could you give a few useful function signatures, and their explanation? How does "not" work polymorphically for example?

I did not mean to include functions that take type constructors as parameters (so lists are out of my discussion scope). I am only considering functions that uses type variables that are not restricted by typeclasses.
There is const: const :: a -> b -> a const x _ = x And of course a family of const like functions: const' :: a -> b -> c -> a const' x _ _ = x and so on... Of course const is related to id. There is also undefined: undefined :: a undefined = undefined You can extend this with arguments: f :: a -> b f x = undefined or even: f x = f x and so on ... Is this what you are looking for?
In this setting could you give a few useful function signatures, and their explanation? How does "not" work polymorphically for example?
not isn't polymorphic in Haskell 98. Cheers, Bernie.

I did not mean to include functions that take type constructors as parameters (so lists are out of my discussion scope). I am only considering functions that uses type variables that are not restricted by typeclasses.
There is const:
const :: a -> b -> a const x _ = x
And of course a family of const like functions:
const' :: a -> b -> c -> a const' x _ _ = x
and so on...
Of course const is related to id.
There is also undefined:
undefined :: a undefined = undefined
You can extend this with arguments:
f :: a -> b f x = undefined
or even:
f x = f x
and so on ...
Is this what you are looking for?
Yes, I thought about these too. Do you find these functions practically useful? Can you give an example where I can utilize these functions? Thanks for the response

G'day. On Mon, Mar 03, 2003 at 11:49:29AM +0200, Cagdas Ozgenc wrote:
Yes, I thought about these too. Do you find these functions practically useful? Can you give an example where I can utilize these functions?
Functions like this are useful for plugging into higher-order functions to tailor them for your specific needs. Here's an artificial example: length = sum . map (const 1) Cheers, Andrew Bromage

Cagdas Ozgenc quotes himself (and somebody else):
Is identity function the only meaningful function one can write without constraining the type variable using a typeclass? If not, could you please give a counter-example?
Certainly you can write lots of ``meaningful function''s without type classes: not, (&&), (||), as well as many more complicated functions at more complicated types.
You can also write useful polymorphic functions without type classes, as long as you specify at least one type. ...
I'm somewhat curious, though: why do you ask this question? How do you expand your question that makes the answer seem to be ``no''?
I did not mean to include functions that take type constructors as parameters (so lists are out of my discussion scope). I am only considering functions that uses type variables that are not restricted by typeclasses. In this setting could you give a few useful function signatures, and their explanation? How does "not" work polymorphically for example?
Bernard James POPE proposes:
There is const: const :: a -> b -> a const x _ = x And of course a family of const like functions: const' :: a -> b -> c -> a const' x _ _ = x and so on...
There is also undefined: undefined :: a undefined = undefined ...
Is this what you are looking for?
==== My three eurocents. I believe that the Author of the original query won't care more about undefined stuff than most of us. He wants truly polymorphic functions, of the type, say, a->b->a etc., without constraints. The answer exists, although it is not always trivial to find interesting examples. Imagine a (postulated) polymorphic type, say, (a->b)->(b->a) . Consider the symbol (->) to be an implication in logic. Ask yourself; "is it a tautology, valid for *any* objects of types "a" or "b"? If yes, then this is a type, and you can in principle find a model for it. Example: composition type: (a->b)->(c->a) -> (c->b) function: (.) (.) f g x = f (g x) On the other hand the "type" (a->b) is *NOT* a valid theorem. This is not a type. You won't find any model of it. No, no, get out with your f x = undefined. The "subst" combinator: subst f g x = f x (g x) has the type (a->b->c) -> (a->b) -> a -> c (unless I've produced some mistake) You can sing the rest of this solemn song yourself, you know the basic tune. Read Luca Cardelli papers, Wadler's "Theorems for free", etc. Jerzy Karczmarczuk

My three eurocents. I believe that the Author of the original query won't care more about undefined stuff than most of us. He wants truly polymorphic functions, of the type, say, a->b->a etc., without constraints.
The answer exists, although it is not always trivial to find interesting examples.
Imagine a (postulated) polymorphic type, say, (a->b)->(b->a) . Consider the symbol (->) to be an implication in logic. Ask yourself; "is it a tautology, valid for *any* objects of types "a" or "b"? If yes, then this is a type, and you can in principle find a model for it.
Example: composition
type: (a->b)->(c->a) -> (c->b) function: (.) (.) f g x = f (g x)
On the other hand the "type" (a->b) is *NOT* a valid theorem. This is not a type. You won't find any model of it. No, no, get out with your f x = undefined.
The "subst" combinator: subst f g x = f x (g x) has the type
(a->b->c) -> (a->b) -> a -> c (unless I've produced some mistake)
The time you grouped (a->b->c), you utilized the arrow type constructor to build a function type, it is no different that using a polymorphic list. I think I am not happy with the dual semantics of this arrow thingie. I have to ponder on this some more, I guess. Thanks for the response. Greatly appreciated.

The time you grouped (a->b->c), you utilized the arrow type constructor to build a function type, it is no different that using a polymorphic list. I think I am not happy with the dual semantics of this arrow thingie. I have to ponder on this some more, I guess.
Thanks for the response. Greatly appreciated.
I'm not a student of type theory, but what follows is my own attempt to rigorously (per my definitions) formalize an answer. Lets forget about the undefined, bottom, error, or whatever cases and look at the following. Lets think about this inductively. First off, lets start off with something of type a. (here we don't mean that something of type forall a . a, which is a whole different type, we just mean we have something with a specific type, we just don't care what it is.) Now, with the arrow constructor we can build two new types of functions. a - > a (of which the only useful function I can see is id or a constant function which constrains the type of the 1st argument.) b -> a (which is basically a constant function.) we can continue to build new functions by either adding an existing type variable from the list of expressions we have created, or introducing the new type variable c. so now we have U: a -> a -> a V: a -> b -> a W: b -> a -> a X: b -> b -> a Y: c -> a -> a Z: c -> b -> a Analyzing the functions (U..Z) we find: U: could be any any thing similar to asTypeOf (selecting either the first or second arguments, constraining them to a single type,) or a constant valued function. V: choose first argument or constant W: choose second argument or constant. X: constant f (But could this be a very odd but perhaps minorly useful method to constrain types of certain values in some type system?) Lets call this a constant asTypeOf function Y: whoops! this is isomprophic to W Z: constant f Now, if we go on creating 3,4,...,N parameter, etc. functions, could we find anything other than functions which could not described described as some combination of the following? (Assume i is integer and z_i is just a specific argumetn number). 1: selecting asTypeOf function (with type constraint a on arguments (y_1,y_2, ...), type constraint b on arguments (z_1,z_2, ....), type constraint c ....) I am considering id as one of these, since it selects its first (and only) argument. 2: constant asTypeOf function with type constraints similar to that of case 1. 3: constant function without type constraints. This is where induction can get confusing, because we need to deal with 6 cases. existing type var on cases 1,2, and 3, and new type var on cases 1,2,and 3. I will denote the cases et1,et2,et3 and nt1,nt2,nt3 respectively. et1: just (possibly*) adds a new type constraint to new function et2: just (possibly*) adds a new type constraint to new function et3: now we have a constraint on the type, so the new function is a case 2 function. nt1: no new type constraint nt2: no new type constraint nt3: no new type constraint (Th new function is a constant function without type constraints). * I say possibly here because in the case where you selected a type var amongst the set of type vars which are already declared in your list of created functions, and add it to a function which does not have that type var, it would be the same as adding a new type var. If this is confusing, just consider cases W and Y a from few paragraphs above (where meantion, "whoops, this is isomorphic...") and maybe you'll understand what I'm trying to say. So it looks like you only get those three cases if you go by my partitioning of the kinds of functions. Jay Cox

On Monday, 2003-03-03, 10:00, CET, Cagdas Ozgenc wrote:
[...]
I did not mean to include functions that take type constructors as parameters (so lists are out of my discussion scope). I am only considering functions that uses type variables that are not restricted by typeclasses. In this setting could you give a few useful function signatures, and their explanation?
In the prelude: error :: String -> a
[...]
Wolfgang
participants (7)
-
Andrew J Bromage
-
Bernard James POPE
-
Cagdas Ozgenc
-
Jay Cox
-
Jerzy Karczmarczuk
-
Jon Cast
-
Wolfgang Jeltsch