有没有一个“单位”?班级?有用吗?

发布于 2024-12-04 18:49:28 字数 450 浏览 2 评论 0原文

是否有一个类用于具有单个单位值的类型(不确定这里的术语是否正确),即具有一些预定义的类型?

class Unit a where
    unit :: a

instance Unit () where
    unit = ()

instance Unit (Maybe a) where
    unit = Nothing

...对于所有 Monoids、MonadPlus 等。

我想该类的另一个名称可能是 Default。这对我来说最近两次很有用。

可能没有令人信服的例子:

extract :: (Unit r)=> Reader r a -> a
extract r = runReader r unit

这存在吗?其他人认为这可能有用吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

我一直都在从未离去 2024-12-11 18:49:28

是的,它可能会有用。事实上,它的所有稍微不兼容的版本都是有用的!这有点问题。

甚至不清楚这样的类意味着什么,这使得实际使用变得困难,因为不可避免地你会遇到有多种默认值选择的类型,并且如果不立即清楚哪个默认值一旦实例提供了,您几乎就失去了首先拥有该类的所有好处。

几个例子:

  • 对于 Monoid 实例,您显然希望恒等元素是默认值。但现在您又回到了许多类型具有两个或更多合理 Monoid 实例的问题。默认的Integer是0还是1?对于 Monoid,标准库使用 newtype 包装器,但这些包装器很笨拙,并且很难使用包装的类型 - 使用 Monoid 它工作正常,因为您可以访问 mconcat 等,但您无法仅使用默认值任何有趣的事情。

  • 对于具有“空”值的仿函子类型,这给出了明显的默认值。这就是 MonadPlusAlternative 正在做的事情......并且也与 Monoid 重叠,如果我没记错的话,至少有一种类型三个实例并不相同。当有多个选择时,你会选择哪一个?考虑列表:您可以盲目地附加它们,给出任意 Monoid,并以空列表作为标识;但对于Monoids列表,您还可以zipWith mappend,给出一个以repeat mempty为标识的提升的monoid。 都有类似的 Monoid 实例,但并不总是两者都有 - 因此,无论您选择哪个列表,您都会在概念上与其他一些 Functor 不一致!

  • 对于像 () 这样的单位类型,选择默认值并不难!但是枚举呢?选择第一个构造函数有意义吗?有时,但并非总是如此。使用该类的人如何知道?

  • 有界怎么样?如果以上都不适用,您可以使用 minBound。但上述某些类型也可能是Bounded,因此如果它们的默认值不是最小值,您就会感到困惑。

基本上,有足够的重叠,似乎有意义......但实际上,您在这里至少想到了三种不同的类型类,并且尝试统一它们可能没有那么有帮助乍一看似乎是这样。


如果您可以更好地确定事情并为“默认”值提供清晰、一致的语义解释,而无需只需重新发明 Monoid 或另一个现有类,这样类型类很容易使用,无需停下来思考选择什么“默认”,太棒了!但我不会对它发挥作用抱有希望。

也就是说,任何标准类型类都没有涵盖的一个明显合理的情况是像 () 这样的单例。大多数时候,这些并不是很有用——原因很明显!——这可能就是没有这样一个类的原因。然而,这样的类非常有用的一个地方是,当您正在做一些涉及类型级别的恶作剧时,因为这样的类型在类型和术语级别上都表示单个值 - 所以此类类型的类可以让您自由地操作类型级值,然后想出与之相关的术语,这样您就可以将其传递给其他一些函数,例如,可以根据它选择一个类型类实例。因此,我在我永远不完整的类型黑客库中有一个类似的类,例如:

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)

不过,我怀疑这样的类在任何其他上下文中都非常有用。

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 sensible Monoid instances. Is the default Integer 0 or 1? For Monoid, the standard library uses newtype wrappers, but those are clumsy and make it difficult to work with the wrapped types--with Monoid it works okay because you get access to mconcat 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 what MonadPlus and Alternative are doing... and also overlaps with Monoid, 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 arbitrary Monoid, with the empty list as identity; but for lists of Monoids you can also zipWith mappend, giving a lifted monoid with repeat mempty as the identity. Many functors have analogous Monoid instances, but not always both--so whichever you pick for lists, you'll be conceptually inconsistent with some other Functor!

  • 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 use minBound. But some of the above types could be Bounded 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.

╰ゝ天使的微笑 2024-12-11 18:49:28

您正在寻找某种 Default 类型类。虽然“默认”的语义应该是什么是有争议的(我建议您接受 CA McCanns 的回答,因为他在那里提出了宝贵的意见),您可以从一个相当常用的名为 Default 的包中获取一个 Default 类。 a href="http://hackage.haskell.org/package/data-default-0.3.0" rel="nofollow">data-default

班级是:

-- | A class for types with a default value.
class Default a where
    -- | The default value for this type.
    def :: a

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
影子的影子 2024-12-11 18:49:28

如果您想避免使用新类,可以根据 Enum 类定义单位:

unit :: Enum a => a
unit = toEnum 0

或者使用 Bounded 类可能更好:

unit :: Bounded a => a
unit = minBound

这两者都会产生单位类型的预期结果(并且很可能适用于任何其他单个构造函数类型) :

*Main> unit :: ()
()

与 data-default 类(在另一个答案中提到)相比,缺点是实例较少,特别是没有为 [a] 返回 [] 的实例。此外,结果也不是您所期望的某种类型的结果,特别是如果您使用 minBound:

*Main> unit :: Int
-2147483648

*Main> unit :: Char
'\NUL'

*Main> unit :: Bool
False

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文