Problem trying to get class Bounded to work

Hi - I've got the following function which doesn't compile: createMonoMultiFont :: (MonadException m, Enum i, Bounded i) => [(i, Font, Colour)] -> m (MonoMultiFont i) createMonoMultiFont elements = liftIO . block $ do mmfRaw <- duma_MonoMultiFont_create (fromEnum (maxBound::i)) mmfPtr <- newForeignPtr duma_MonoMultiFont_Release mmfRaw return $ MonoMultiFont mmfPtr The problem is that ghc complains that there is no instance for Bounded i even though I've written "Bounded i" in the type declaration for the function. I was expecting that I could use the same type variables in the function body as in the type signature so that I could use maxBound::i to find the max bound of whatever type is instantiated to i. What am I doing wrong here, and is it possible to create such a function? Thanks, Brian.

Brian Hulley wrote:
Hi - I've got the following function which doesn't compile:
createMonoMultiFont :: (MonadException m, Enum i, Bounded i) => [(i, Font, Colour)] -> m (MonoMultiFont i) createMonoMultiFont elements = liftIO . block $ do mmfRaw <- duma_MonoMultiFont_create (fromEnum (maxBound::i)) mmfPtr <- newForeignPtr duma_MonoMultiFont_Release mmfRaw return $ MonoMultiFont mmfPtr
The problem is that ghc complains that there is no instance for Bounded i even though I've written "Bounded i" in the type declaration for the function.
Here is a gross hack that compiles: createMonoMultiFont elements = liftIO . block $ do let (onei,_,_):_ = (undefined, undefined, undefined) : elements upper :: Bounded a => a -> a upper _ = maxBound last = upper onei mmfRaw <- duma_MonoMultiFont_create (fromEnum last) -- adding of elements to mmfRaw ommitted for clarity mmfPtr <- newForeignPtr duma_MonoMultiFont_Release mmfRaw return $ MonoMultiFont mmfPtr Still, it would be nice to be able to just write (maxBound::i) since the dictionary for i is present in the result. Brian.

Hello,
There's a prelude function called asTypeOf which might help here:
asTypeOf :: a -> a -> a
asTypeOf = const
You can use maxBound `asTypeOf` (fst3 . head $ elements)
Another option is GHC's scoped type variables, which you use by adding
a type signature to your pattern. See:
http://www.haskell.org/ghc/docs/latest/html/users_guide/type-extensions.html...
- Cale
On 22/05/06, Brian Hulley
Brian Hulley wrote:
Hi - I've got the following function which doesn't compile:
createMonoMultiFont :: (MonadException m, Enum i, Bounded i) => [(i, Font, Colour)] -> m (MonoMultiFont i) createMonoMultiFont elements = liftIO . block $ do mmfRaw <- duma_MonoMultiFont_create (fromEnum (maxBound::i)) mmfPtr <- newForeignPtr duma_MonoMultiFont_Release mmfRaw return $ MonoMultiFont mmfPtr
The problem is that ghc complains that there is no instance for Bounded i even though I've written "Bounded i" in the type declaration for the function.
Here is a gross hack that compiles:
createMonoMultiFont elements = liftIO . block $ do let (onei,_,_):_ = (undefined, undefined, undefined) : elements upper :: Bounded a => a -> a upper _ = maxBound last = upper onei
mmfRaw <- duma_MonoMultiFont_create (fromEnum last) -- adding of elements to mmfRaw ommitted for clarity mmfPtr <- newForeignPtr duma_MonoMultiFont_Release mmfRaw return $ MonoMultiFont mmfPtr
Still, it would be nice to be able to just write (maxBound::i) since the dictionary for i is present in the result.
Brian.
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Cale Gibbard wrote:
Hello,
There's a prelude function called asTypeOf which might help here: asTypeOf :: a -> a -> a asTypeOf = const
You can use maxBound `asTypeOf` (fst3 . head $ elements)
Another option is GHC's scoped type variables, which you use by adding a type signature to your pattern. See: http://www.haskell.org/ghc/docs/latest/html/users_guide/type-extensions.html...
Hi Cale - Thanks for the solutions. The GHC scoped typed variables seem to be the best solution since they would seem to be the only way to implement something where the type only occurs in the result eg (if the actual fonts were to be added later): createMonoMultiFont :: forall i m . (MonadException m, Enum i, Bounded i) => m (MonoMultiFont i) Thanks, Brian.

Hello Brian, Tuesday, May 23, 2006, 4:05:45 AM, you wrote:
Thanks for the solutions. The GHC scoped typed variables seem to be the best solution since they would seem to be the only way to implement something where the type only occurs in the result eg (if the actual fonts were to be added later):
createMonoMultiFont :: forall i m . (MonadException m, Enum i, Bounded i) => m (MonoMultiFont i)
malloc :: Storable a => IO (Ptr a) malloc = doMalloc undefined where doMalloc :: Storable b => b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy) these tricks are better if you need to write code that will be compiled not only by GHC. and i also hope that Haskell' will support scoping of type variables over the function body -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Bulat Ziganshin wrote:
malloc :: Storable a => IO (Ptr a) malloc = doMalloc undefined where doMalloc :: Storable b => b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy)
Is there any reason to not code this as malloc :: Storable a => IO (Ptr a) malloc = mallocBytes $ sizeof undefined ? Jacques

Jacques Carette wrote:
Bulat Ziganshin wrote:
malloc :: Storable a => IO (Ptr a) malloc = doMalloc undefined where doMalloc :: Storable b => b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy)
Is there any reason to not code this as
malloc :: Storable a => IO (Ptr a) malloc = mallocBytes $ sizeof undefined
What type would the 'undefined' have in this context? sizeOf has type Storable a => a -> Int -- that doesn't help. The purpose of doMalloc is to force the type checker to choose the right type for that 'undefined'. regards, Bertram

Bertram Felgenhauer wrote:
Jacques Carette wrote:
Bulat Ziganshin wrote:
malloc :: Storable a => IO (Ptr a) malloc = doMalloc undefined where doMalloc :: Storable b => b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy)
Is there any reason to not code this as
malloc :: Storable a => IO (Ptr a) malloc = mallocBytes $ sizeof undefined
What type would the 'undefined' have in this context?
sizeOf has type Storable a => a -> Int -- that doesn't help.
The purpose of doMalloc is to force the type checker to choose the right type for that 'undefined'.
I still don't quite see it! To my eyes, the type of doMalloc and the type of malloc look exactly "the same", with neither providing more specificity than the other. Also, the 'undefined' can be of any type, as long as it is from class Storable. So it is a placeholder for a dictionary - what else does it need? [Constraint-typing-wise, it doesn't need anything else, but perhaps the issue is that I don't understand Haskell's type system quite well enough]. I guess I am also asking "Is there a GOOD reason why the simpler code can't be used?". Or maybe what I really need is malloc :: Storable a => IO (Ptr a) malloc = mallocBytes $ sizeof undefined::a ? That would make sense to me, since in a more general function I might have 2 instances (Storable a, Storable b) available, and 2 undefineds, and I could not know how to match them up deterministically in all cases. I guess I prefer a type annotation over a dummy function that is there just to force the type checker to believe me. If one has to force the type checker, may as well do it with a type, not code! Jacques

Hello Jacques, Tuesday, May 23, 2006, 7:13:33 PM, you wrote:
malloc :: Storable a => IO (Ptr a) malloc = doMalloc undefined where doMalloc :: Storable b => b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy) Is there any reason to not code this as
malloc :: Storable a => IO (Ptr a) malloc = mallocBytes $ sizeof undefined
well 1) in your code type of 'undefined' is unknown. it can be any type, while we need 'undefined' of one concrete type, which is given in function signature. compiler will just complain that type is not fixed here 2) what we really need is to write malloc :: Storable a => IO (Ptr a) malloc = mallocBytes $ sizeof (undefined::a) i.e. fix type of 'undefined' to 'a'. but that is impossible in Haskell-98 3) GHC provides language extension (see above in the thread), that allow to use 'a' in function body if function was declared using 'forall', so this works in GHC malloc :: forall a . Storable a => IO (Ptr a) malloc = mallocBytes $ sizeof (undefined::a) 4) this example is borrowed from std libs, what is common for hugs, ghc and nhc, so they can't use ghc-specific solution. instead, this complex trick is used. it's very unobvious, but at least it works with any H98-compatible compiler -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Jacques Carette wrote:
I guess I prefer a type annotation over a dummy function that is there just to force the type checker to believe me. If one has to force the type checker, may as well do it with a type, not code!
I agree totally. Also, I think there is a more general issue regarding the relative importance of type inference when programming, since it seems to me that a lot of awkwardness is introduced into Haskell by the insistence that the type inference algorithm should be able to infer everything by itself to surprise the programmer with knowledge that the programmer should already know in the first place and could quite easily have written down directly. An example is the fact that constructors and record field names currently have to be unique within a whole module. If instead of this, constructors and field names were always in seperate namespaces indexed by type, instead of them determining the type, programming would be tremendously easier because the same names could be reused without worrying about conflicts. eg compare data Either a b = Left a | Right b -- Prelude got there first as usual data Location = Location_Left | Location_Right foo (Location_Left, Location_Left, Location_Left) = Left 5 foo (Location_Left, Location_Left, Location_Right) = Right "something" -- 6 more wordy patterns with data Either a b = Left a | Right b data Location = Left | Right -- all names local to type Location foo :: (Location, Location, Location) -> Either Int String foo (Left, Left, Left) = Left 5 foo (Left, Left, Right) = Right "something" -- 6 more simple patterns Regards, Brian.

On May 23, 2006, at 11:13 AM, Jacques Carette wrote:
Bertram Felgenhauer wrote:
Jacques Carette wrote:
Bulat Ziganshin wrote:
malloc :: Storable a => IO (Ptr a) malloc = doMalloc undefined where doMalloc :: Storable b => b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy)
Is there any reason to not code this as
malloc :: Storable a => IO (Ptr a) malloc = mallocBytes $ sizeof undefined
What type would the 'undefined' have in this context?
sizeOf has type Storable a => a -> Int -- that doesn't help.
The purpose of doMalloc is to force the type checker to choose the right type for that 'undefined'.
I still don't quite see it! To my eyes, the type of doMalloc and the type of malloc look exactly "the same", with neither providing more specificity than the other. Also, the 'undefined' can be of any type, as long as it is from class Storable. So it is a placeholder for a dictionary - what else does it need? [Constraint- typing-wise, it doesn't need anything else, but perhaps the issue is that I don't understand Haskell's type system quite well enough].
I guess I am also asking "Is there a GOOD reason why the simpler code can't be used?". Or maybe what I really need is
malloc :: Storable a => IO (Ptr a) malloc = mallocBytes $ sizeof undefined::a
? That would make sense to me, since in a more general function I might have 2 instances (Storable a, Storable b) available, and 2 undefineds, and I could not know how to match them up deterministically in all cases.
I guess I prefer a type annotation over a dummy function that is there just to force the type checker to believe me. If one has to force the type checker, may as well do it with a type, not code!
Sure, and there's good reasons to prefer this. It's not Haskell 98, however. In Haskell 98 type variables in type signatures are not in scope in the body of the function. Adding the type annotation (undefined::a) is the same as (undefined::forall a. a) because free type variables are automatically bound with universal quantifiers. Obviously, that isn't what you want here. So instead one has to play games with the type checker to force the right things to unify. Another thing you'll see sometimes is lazy pattern matching and 'asTypeOf' used to get at type variables inside data types. GHC has an extension to allow scoped type variables and there's also a Haskell' ticket for it. http://www.haskell.org/ghc/docs/latest/html/users_guide/type- extensions.html#scoped-type-variables http://hackage.haskell.org/trac/haskell-prime/wiki/ScopedTypeVariables Rob Dockins Speak softly and drive a Sherman tank. Laugh hard; it's a long way to the bank. -- TMBG

Jacques Carette wrote:
Bulat Ziganshin wrote:
malloc :: Storable a => IO (Ptr a) malloc = doMalloc undefined where doMalloc :: Storable b => b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy)
Is there any reason to not code this as
malloc :: Storable a => IO (Ptr a) malloc = mallocBytes $ sizeof undefined ?
Yes. The supreme clevernesss of Bulat's trick is that doMalloc unifies the type variable 'a' with the type of the argument to sizeOf, whereas "sizeOf undefined" would be ambiguous because the type of "undefined" is not constrained there ie: malloc :: Storable a => IO (Ptr a) malloc = doMalloc undefined where doMalloc :: Storable b => b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy) (doMalloc undefined) :: IO (Ptr a) ==> (doMalloc dummy) :: IO (Ptr a) ==> -- doMalloc :: Storable b => b -> IO (Ptr b) dummy :: a whereas: malloc = mallocBytes $ sizeOf undefined malloc :: IO (Ptr a) ==> (mallocBytes (sizeOf undefined)) :: IO (Ptr a) ==> (sizeOf undefined) :: Int ==> undefined :: unconstrained Regards, Brian.

Hello Brian, Tuesday, May 23, 2006, 7:10:23 PM, you wrote:
Yes. The supreme clevernesss of Bulat's trick is that doMalloc unifies the
my main trick is that i read sources of std libs :) -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Jacques Carette wrote:
Bulat Ziganshin wrote:
malloc :: Storable a => IO (Ptr a) malloc = doMalloc undefined where doMalloc :: Storable b => b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy)
Is there any reason to not code this as
malloc :: Storable a => IO (Ptr a) malloc = mallocBytes $ sizeof undefined ?
There is. Don't you think there's a reason why doMalloc above has an explicit type signature? What would happen if you deleted it? Udo. -- "You, sir, are nothing but a pathetically lame salesdroid! I fart in your general direction!" -- Randseed on #Linux

See "7.4.10.3 Declaration type signatures" in the manual: http://haskell.org/ghc/docs/6.4.2/html/users_guide/type-extensions.html#decl... At the moment, the "i" in the type declaration and in the body are not the same "i". To get the "i" from the type declaration for createMonoMultiFont into the scope of the definition, the trick is to add the otherwise useless/forbidden "forall i m." to the type declaration: createMonoMultiFont :: forall i m.(MonadException m, Enum i, Bounded i) => [(i, Font, Colour)] -> m (MonoMultiFont i) createMonoMultiFont elements = liftIO . block $ do mmfRaw <- duma_MonoMultiFont_create (fromEnum (maxBound::i)) mmfPtr <- newForeignPtr duma_MonoMultiFont_Release mmfRaw return $ MonoMultiFont mmfPtr The above is now accepted by ghci on my GHC 6.4.1 installation on OS X. -- Chris Brian Hulley wrote:
Hi - I've got the following function which doesn't compile:
createMonoMultiFont :: (MonadException m, Enum i, Bounded i) => [(i, Font, Colour)] -> m (MonoMultiFont i) createMonoMultiFont elements = liftIO . block $ do mmfRaw <- duma_MonoMultiFont_create (fromEnum (maxBound::i)) mmfPtr <- newForeignPtr duma_MonoMultiFont_Release mmfRaw return $ MonoMultiFont mmfPtr
The problem is that ghc complains that there is no instance for Bounded i even though I've written "Bounded i" in the type declaration for the function.
I was expecting that I could use the same type variables in the function body as in the type signature so that I could use maxBound::i to find the max bound of whatever type is instantiated to i.
What am I doing wrong here, and is it possible to create such a function?
Thanks, Brian. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Chris Kuklewicz wrote:
See "7.4.10.3 Declaration type signatures" in the manual: http://haskell.org/ghc/docs/6.4.2/html/users_guide/type-extensions.html#decl...
At the moment, the "i" in the type declaration and in the body are not the same "i".
To get the "i" from the type declaration for createMonoMultiFont into the scope of the definition, the trick is to add the otherwise useless/forbidden "forall i m." to the type declaration:
createMonoMultiFont :: forall i m.(MonadException m, Enum i, Bounded i) => [(i, Font, Colour)] -> m (MonoMultiFont i) createMonoMultiFont elements = liftIO . block $ do mmfRaw <- duma_MonoMultiFont_create (fromEnum (maxBound::i)) mmfPtr <- newForeignPtr duma_MonoMultiFont_Release mmfRaw return $ MonoMultiFont mmfPtr
The above is now accepted by ghci on my GHC 6.4.1 installation on OS X.
Thanks - I'd completely forgotten about the need to add an explicit forall, because although I read the section a while ago, the need for the forall got clouded over in my mind by the fact that the forall is unnecessary for the type signature itself, so the need for the forall seems to be just a good way to slot this extra functionality into the language while remaining backwards compatible with H98. I wonder if Haskell' will automatically make type variables scope over the function body (without an explicit forall)? Is there any reason why they shouldn't always do so? Thanks again, Brian.

On Tuesday 23 May 2006 02:00, Brian Hulley wrote:
I wonder if Haskell' will automatically make type variables scope over the function body (without an explicit forall)? Is there any reason why they shouldn't always do so?
One I can think of is that the type signature for a function may be at the other end of the file. Some discussion about the pros and cons can be found here http://www.mail-archive.com/haskell%40haskell.org/msg15738.html and here http://article.gmane.org/gmane.comp.lang.haskell.glasgow.user/9219 Ben -- You've never looked into my eyes but don't you want to know What the dark and the wild and the different know -- Melissa Etheridge

Benjamin Franksen wrote:
On Tuesday 23 May 2006 02:00, Brian Hulley wrote:
I wonder if Haskell' will automatically make type variables scope over the function body (without an explicit forall)? Is there any reason why they shouldn't always do so?
One I can think of is that the type signature for a function may be at the other end of the file. Some discussion about the pros and cons can be found here
http://www.mail-archive.com/haskell%40haskell.org/msg15738.html
and here
http://article.gmane.org/gmane.comp.lang.haskell.glasgow.user/9219
Thanks for the links. I hadn't realised there were so many hard choices to make in the design of the type system and that it is not clear that there is a unique correct answer to the problem. I'll have to do some further reading to find the meaning of "rigid" and "wobbly" and "non-type type variables" or type variables which are not types... ;-) I must admit I also don't understand why there is a difference between let bindings and case bindings. (I know let bindings are lazy but I don't see what difference this makes to the type system) Or is the difference just due to the desire to still have a monomorphism restriction and/or limitations with HM type inference? Would the difference be needed if a different type system (eg intersection types with principal *typing* property instead of HM types which only have a principal types property) were used? I was very surprised at the decision to use the otherwise redundant "forall" to make a distinction between whether or not type variables are scoped, since a simple compiler option could have been used to turn on/off implicit scoping instead to preserve compatibility with H98 modules. One issue seems to be that H98 compatibility (eg implicit forall) is fundamentally incompatible with a simple uniform approach to typing in the presence of all the advanced type extensions that have been added since H98 was designed. It still seems there are too many different cases. Perhaps there are conflicting demands on the type system and the rejection of some of these demands would lead to a simpler solution? Robert Dockins wrote:
GHC has an extension to allow scoped type variables and there's also a Haskell' ticket for it.
http://www.haskell.org/ghc/docs/latest/html/users_guide/type-extensions.html... http://hackage.haskell.org/trac/haskell-prime/wiki/ScopedTypeVariables
Hopefully the ticket will get more detailed and concrete and rationally-reconstructed to remove the existing quirks and "thence grow to a point" as Shakespeare might have said, in due course. Regards, Brian.
participants (9)
-
Benjamin Franksen
-
Bertram Felgenhauer
-
Brian Hulley
-
Bulat Ziganshin
-
Cale Gibbard
-
Chris Kuklewicz
-
Jacques Carette
-
Robert Dockins
-
Udo Stenzel