我是哈斯克尔的新手。诸如 monad 和 monoid 之类的概念及其相应的类型类非常有趣,但高度抽象和遥远。我想知道这些先进的概念如何使事情更容易实施。一些独立的具体例子很高兴看到。
您可以通过类型和类型类来理解问题域。以下是我通过查看V3其实例可以收集到的信息,而无需考虑该类型用于表示什么(3D 向量)。
Run Code Online (Sandbox Code Playgroud)type V3 :: Type -> Type data V3 a = V3 a a a
这是一个简单的类型。并且不置可否。a对于什么可以是没有任何限制。这就是它的力量的来源,Edward Kmett 有一个关于这些“愚蠢的可重用数据类型”的经典演讲。
V3具有基本属性。这一子句deriving可能为您提供了超过 20 个可操作的函数V3。Foldable相当于定义toList (V3 a b c) = [a, b, c].
Run Code Online (Sandbox Code Playgroud)deriving stock (Functor, Foldable, Traversable)
我专注于类型构造函数类。我们可以自动派生许多类型类,但这些实例很少说明V3. 它们是相反的指示符,如果类型没有派生Eq,那么可能是因为无法定义该类型,或者因为它使用了值得注意的非标准定义。
Run Code Online (Sandbox Code Playgroud)deriving stock (Eq, Ord, Show, Read, Data, Generic, Generic1, Lift)
然后我发现这是Representable有道理的,因为可表示表示一种“静态形状”。实际上可表示的手段V3 a是(同构)一个函数!
Run Code Online (Sandbox Code Playgroud)type Index3 :: Type data Index3 = O | I | II instance Representable V3 where type Rep V3 = Index3 index :: V3 a -> (Index3 -> a) index (V3 a b c) = \case O -> a I -> b II -> c tabulate :: (Index3 -> a) -> V3 a tabulate make = V3 (make O) (make I) (make II)
V3 1.0 2.0 3.0你可以写tabulate vec:
vec :: Index3 -> Double
vec = \case
O -> 1.0
I -> 2.0
II -> 3.0
Run Code Online (Sandbox Code Playgroud)
证明 与(= ) V3(自然地)同构是很重要的。我们对函数非常熟悉,我们可以通过同构来继承任何函数实例。(Index3 ->)Representable
这条规则是通过它的函数表示来封装的Co,你可以列出所有可能的实例给我们( )V3:instances Co V3
> :instances Co V3
instance Applicative (Co V3)
instance Functor (Co V3)
instance Monad (Co V3)
instance Apply (Co V3)
instance Bind (Co V3)
instance Distributive (Co V3)
instance Representable (Co V3)
Run Code Online (Sandbox Code Playgroud)
所以我们可以推导Applicative,Monad至少从中可以得出,如果Index3是一个 Monoid,我们可以定义一个Comonad
Run Code Online (Sandbox Code Playgroud)deriving (Applicative, Monad) via Co V3
我们还看到很多实例都是它们自己的代数的提升版本。
Run Code Online (Sandbox Code Playgroud)instance Num a => Num (V3 a) where (+) = liftA2 (+) (-) = liftA2 (-) (*) = liftA2 (*) negate = fmap negate abs = fmap abs signum = fmap signum fromInteger = pure . fromInteger instance Semigroup a => Semigroup (V3 a) where (<>) = liftA2 (<>) instance Monoid a => Monoid (V3 a) where mempty = pure mempty
这可以概括为Ap V3 a允许在任何Applicative.
Run Code Online (Sandbox Code Playgroud)deriving (Num, Semigroup, Monoid) via Ap V3 a
我已经彻底描述了这个简单类型的特征,即使我以前从未见过这个数据类型,对我来说也会很清楚。实例(和非实例)赋予它特征,而派生就像一个执行摘要,以完全正确的细节级别告诉您正在发生的事情。
定义后,V3您现在可以TicTacToe通过两个向量的组合来定义一个板,这两个TicTacToe向量定义为与 中的函数同构(Index3, Index3)。我们可以使用 构建一个空板并使用pure Nothing :: TicTacToe (Maybe Player)索引到板中(Index3, Index3)。
type TicTacToe :: Type -> Type
newtype TicTacToe a = TicTacToe (V3 (V3 a))
deriving (Functor, Foldable, Applicative, Representable)
via Compose V3 V3
deriving (Monad)
via Co TicTacToe
deriving (Num, Semigroup, Monoid)
via Ap TicTacToe a
-- > index board (O, O)
-- Nothing
-- > index board (O, II)
-- Just Player2
board :: TicTacToe (Maybe Player)
board = TicTacToe do
V3 do V3 Nothing Nothing (Just Player2)
do V3 Nothing Nothing Nothing
do V3 Nothing Nothing (Just Player1)
Run Code Online (Sandbox Code Playgroud)
整个故事重演,TicTacToe现在是 a Monad,数字和幺半群运算可以在它之上解除。