Is there a "unit" class? Would it be useful?
Is there a class for types having a single unit value (not sure of the correct terminology here) i.开发者_运维知识库e. types with some pre-defined value?
class Unit a where
unit :: a
instance Unit () where
unit = ()
instance Unit (Maybe a) where
unit = Nothing
...for all Monoids, MonadPlus, etc.
I suppose another name for the class might be Default
. This would have been useful twice recently now for me.
Probably unconvincing example:
extract :: (Unit r)=> Reader r a -> a
extract r = runReader r unit
Does this exist? Do others think it might be useful?
Yes, it would probably be useful. In fact, all of the slightly incompatible versions of it would be useful! Which is kinda the problem.
It's not clear what such a class would even mean, which makes it hard to actually use, because inevitably you'll hit types where there are multiple choices of a default value, and if it's not immediately clear which one the instance provides, you pretty much lose all benefit of having the class in the first place.
A few examples:
For
Monoid
instances, you'd obviously expect the identity element to be the default. But now you're back to the problem of so many types having two or more sensibleMonoid
instances. Is the defaultInteger
0 or 1? ForMonoid
, the standard library usesnewtype
wrappers, but those are clumsy and make it difficult to work with the wrapped types--withMonoid
it works okay because you get access tomconcat
and such, but you can't do anything interesting with just a default value.For
Functor
-like types with an "empty" value, that gives an obvious default. This is whatMonadPlus
andAlternative
are doing... and also overlaps withMonoid
, and if memory serves me there's at least one type where those three instances aren't identical. Which do you pick, when there's more than one choice? Consider lists: You can blindly append them, giving an arbitraryMonoid
, with the empty list as identity; but for lists ofMonoids
you can alsozipWith mappend
, giving a lifted monoid withrepeat mempty
as the identity. Many functors have analogousMonoid
instances, but not always both--so whichever you pick for lists, you'll be conceptually inconsistent with some otherFunctor
!For unit types like
()
, it's not hard to pick a default! But what about enumerations? Does it make sense to pick the first constructor? Sometimes, but not always. How will people using the class know?What about
Bounded
? If none of the above applies, you could useminBound
. But some of the above types could beBounded
as well, so you'll confuse matters if their default isn't their minimum value.
Basically, there's just enough overlap that it seems to make sense... but really, you've got at least three different type classes in mind here, and trying to unify them is probably not as helpful as it seems at first.
If you can pin things down a little better and give a clear, consistent semantic interpretation of a "default" value, without just reinventing Monoid
or another existing class, such that the type class is easy to use without having to stop and think about what "default" gets chosen, great! But I wouldn't get my hopes up on making it work.
That said, the one obviously sensible case that's not covered by any standard type class is singletons like ()
. Most of the time these aren't terribly useful--for obvious reasons!--which is probably why there isn't such a class. One place where such a class is extremely useful, though, is when you're doing something involving type-level shenanigans, because such a type represents a single value at both the type and term level--so a class for such types lets you manipulate type-level values freely, then conjure up the term that goes with it, so you can pass it to some other function that might, e.g., select a type class instance based on it. For that reason, I have a class along those lines in my perpetually-incomplete type-hackery library, e.g.:
class TermProxy t where
term :: t
-- This makes explicit the lexical pun of () having type ().
instance TermProxy () where
term = ()
instance (TermProxy a, TermProxy b) => TermProxy (a, b) where
term = (term, term)
I doubt such a class is very useful in any other context, though.
You are looking for some sort of Default
type class. While the semantics of what a "default" should be is debatable (and I suggest you accept C.A. McCanns answer for his valuable comments there), you can get a Default
class from a fairly commonly used package called data-default
.
The class is:
-- | A class for types with a default value.
class Default a where
-- | The default value for this type.
def :: a
If you want to avoid a new class, you can define unit in terms of the Enum class:
unit :: Enum a => a
unit = toEnum 0
Or maybe better with the Bounded class:
unit :: Bounded a => a
unit = minBound
Both of these produce the expected result for the unit type (and most likely for any other single constructor type):
*Main> unit :: ()
()
The drawbacks compared to the data-default class (mentioned in another answer) is that there are fewer instances, particularly no instance that returns [] for [a]. Also the result is not what you might expect from some type, especially if you use minBound:
*Main> unit :: Int
-2147483648
*Main> unit :: Char
'\NUL'
*Main> unit :: Bool
False
精彩评论