首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >理解“newtype”关键字

理解“newtype”关键字
EN

Stack Overflow用户
提问于 2022-11-16 09:30:59
回答 2查看 94关注 0票数 2

对于uni任务,我们得到了一行Haskell代码,其中显示:

代码语言:javascript
复制
newtype TC a = TC ([Id] -> Either TypeError ([Id], a))

首先,TypeError是需要我们在赋值中实现的东西,所以我不能在这里发布数据声明,但我的问题是。我怎么读上面的代码?anewtype TC之后是什么?我也不明白TC是如何被重用到等号右边的。

我认为a是一个类型变量,因为newtype的工作方式类似于数据。我不知道知道这会对我的理解有什么帮助。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-11-16 09:57:26

anewtype TC之后是什么?

新类型声明中的a

代码语言:javascript
复制
newtype TC a = ...

表达式与函数声明中的x非常类似。

代码语言:javascript
复制
f x = ...

a是一个类型参数。因此,您可以使用TC,例如,TC IntTC Bool,类似于如何使用f (如f 1f "bla bla" )(取决于其类型)。

这种情况下的TC Int相当于以下备选方案:

代码语言:javascript
复制
newtype TCInt = TCInt ([Id] -> Either TypeError ([Id], Int))

我也不明白TC是如何被重用到等号右边的。

在Haskell,这是一个令人困惑的怪癖。实际上,TC并没有被重用,而是声明了两个单独的实体,它们都被称为TC。你也可以用不同的方式称呼它们:

代码语言:javascript
复制
newtype TC_T a = TC_V ([Id] -> Either TypeError ([Id], a))

  • TC_T是一个类型构造函数。这将出现在类型签名中,即TC_T Bool.
  • TC_VTC_T Int是一个值构造函数。当生成TC_T IntTC_T Int类型的值时,可以使用此方法。

因此,例如,您可以编写以下内容:

代码语言:javascript
复制
tci :: TC_T Int
tci = TC_V (\ids -> Right (ids, 37))

tcb :: TC_T Bool
tcb = TC_V (\ids -> Right (reverse ids, False))

您的原始版本如下所示:

代码语言:javascript
复制
tci :: TC Int
tci = TC (\ids -> Right (ids, 37))

tcb :: TC Bool
tcb = TC (\ids -> Right (reverse ids, False))

...but,这里仍然是两个分开的东西,都叫TV。Haskell中的大多数新类型调用类型和值构造函数相同,但通常只有类型构造函数从模块导出,值构造函数作为实现细节保留。

对于datanewtype,大多数情况都是一样的。你还不如

代码语言:javascript
复制
data TC a = TC ([Id] -> Either TypeError ([Id], a))

..。新类型版本的唯一区别是一个微妙的间接:如果它是data,那么编译器就插入一个间接性,它允许更多的懒散,但在这种情况下,这样做几乎没有任何意义。

在实践中,通常只在需要多个构造函数和/或具有多个字段的构造函数时才使用data,而newtype不支持这些构造函数。

票数 6
EN

Stack Overflow用户

发布于 2022-11-16 22:23:45

newtype相对于数据的好处是,您可以通过它的基础类型进行派生:

代码语言:javascript
复制
{-# Language DerivingVia              #-}
{-# Language StandaloneKindSignatures #-}

import Control.Applicative   (Alternative)
import Control.Monad         (MonadPlus)
import Control.Monad.Except  (ExceptT(..), Except)
import Control.Monad.Fix     (MonadFix)
import Control.Monad.State   (StateT(..), MonadState)
import Data.Functor.Identity (Identity(..))
import Data.Kind             (Type)

type    TC :: Type -> Type
newtype TC a = MkTC ([Id] -> Either TypeError (a, [Id]))
  deriving
    ( Functor, Applicative, Alterantive
    , Monad, MonadPlus, MonadState [Id], MonadFix
    )
  via StateT [Id] (Except TypeError)

-- Alternative and MonadPlus rely on these
instance Semigroup TypeError ..
instance Monoid    TypeError ..

我不得不交换元组,但这是因为有三个newtype_s:TC aStateT s m aExceptT。编译器为每个_newtype生成一个实例,见证它与底层类型具有相同的运行时表示。

这被称为代表性相等,并由Coercible见证。

代码语言:javascript
复制
-- instance Coercible (StateT s m a) (s -> m (a, s))
type    StateT :: Type -> (Type -> Type) -> (Type -> Type)
newtype StateT s m a = MkStateT (s -> m (a, s))

-- instance Coercible (ExceptT e m a) (m (Either e a))
type    ExceptT :: Type -> (Type -> Type) -> (Type -> Type)
newtype ExceptT e m a = MkExceptT (m (Either e a))

这意味着TC aStateT [Id] (Except TypeError) a是强制的,因此我们可以使用coerce-based派生策略在它们之间携带实例(比如GeneralizedNewtypeDerivingDerivingVia)。

代码语言:javascript
复制
  TC a
={Coercible}
  [Id] -> Either TypeError (a, [Id])
={Coercible}
  [Id] -> ExceptT TypeError Identity (a, [Id])
={Except e = ExceptT e Identity} 
  [Id] -> Except TypeError (a, [Id])
={Coercible}
  StateT [Id] (Except TypeError) a

GeneralizedNewtypeDeriving只在newtype_s上工作,但这并不意味着DerivingVia只在_newtype_s上工作。您可以为_data开发许多实例,例如,通过TC (Ap TC a)上的Applicative提升方法。

使它成为一个新类型确实可以简化事情,Applicative可以通过状态转换器派生,所以我们不需要手工编写任何实例。

代码语言:javascript
复制
data TC a = MkTC ..
  ..

  deriving (Semigroup, Monoid, Num, Bounded)
  via Ap TC a

TC名称在代码中有两个用途(‘双关’)。我把它们分成两个不同的名字,TC和变压器。

左侧是类型构造函数:

代码语言:javascript
复制
>> :k TC
TC :: Type -> Type
>> :k StateT
StateT :: Type -> (Type -> Type) -> (Type -> Type)
>> :k ExceptT
ExceptT :: Type -> (Type -> Type) -> (Type -> Type)

右边是值构造函数,我添加了一个消除歧义的Mk

代码语言:javascript
复制
>> :t MkTC
MkTC :: ([Id] -> Either TypeError (a, [Id])) -> TC a
>> :t MkStateT
MkStateT :: (s -> m (a, s)) -> StateT s m a
>> :t MkExceptT
MkExceptT :: m (Either e a) -> ExceptT e m a

a :: Type是类型构造函数的参数:

代码语言:javascript
复制
TC   :: Type -> Type
TC a :: Type

如果没有类型参数,所有这些实例都不会进行类型检查,因为所有这些实例都需要一个“一元类型构造函数”(Type -> Type)作为参数。也就是说,由类型参数化的类型:

代码语言:javascript
复制
Functor         :: (Type -> Type) -> Constraint
Applicative     :: (Type -> Type) -> Constraint
Alternative     :: (Type -> Type) -> Constraint
Monad           :: (Type -> Type) -> Constraint
MonadPlus       :: (Type -> Type) -> Constraint
MonadState [Id] :: (Type -> Type) -> Constraint
MonadFix        :: (Type -> Type) -> Constraint
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74458089

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档