cli*_*nux 8 dsl haskell metaprogramming
我对范畴理论的了解并不是很好.所以请耐心等待.
我一直在阅读Monads Made Hardicult
并看到以下定义.
class (Category c, Category d) => Functor c d t where
fmap :: c a b -> d (t a) (t b)
Run Code Online (Sandbox Code Playgroud)
从那个网站我读到Haskell前奏中的Functor类型实际上是一个Endofunctor.(上面类中的类别c和d都是Hask)
看完页面后,我很想知道.如果Haskell使用真正的Functors而不仅仅是Endofunctors,它会更适合元编程吗?
说我们有以下(Js代表Javascript)
data Js a where
litInt :: Integer -> Js Integer
litString :: String -> Js String
var :: String -> Js a
lam :: String -> Js b -> Js (a -> b)
let :: String -> Js a -> Js b -> Js b
ap :: JsFunc a b -> Js a -> Js b
type JsFunc a b = Js (a -> b)
Run Code Online (Sandbox Code Playgroud)
现在回到上面Functor的定义
class (Category c, Category d) => Functor c d t where
fmap :: c a b -> d (t a) (t b)
Run Code Online (Sandbox Code Playgroud)
我们可以将c设为JsFunc,d为Hask并且必须为Js
这将为我们提供Js的Functor.我无法使用Haskell的Endofunctor.
我找不到与此Functor相同方式定义的Apply或Applicative类型的任何内容.那些也可以这样做吗?
如果我错了,请纠正我.但Monad也不能这样做,因为它真的是Endofunctor的一个实例?
嗯,首先——
\n\n\n\n\n...如果它使用真正的 Functors 而不仅仅是 Endofunctors ...
\n
Haskell 函子是真正的函子。只是,该类Functor
不允许任何类型的通用函子,而只允许Hask上的特定函子。
事实上,非内函子很有趣。数学家一直在使用它们。虽然正如你所说,不可能有非 endofunctor monad,但类似的情况Applicative
是可能的:该类实际上只是一个很好的Monoidal functors的 Haskell 接口,它可以在不同的类别之间定义。看起来大约是这样的:
class (Functor r t f, Category r, Category t) => Monoidal r t f where\n pureUnit :: () `t` f ()\n fzipWith :: (a, b) `r` c -> (f a, f b) `t` f c\n
Run Code Online (Sandbox Code Playgroud)\n\n不过,要像标准Applicative
类一样方便地在任何地方实际使用它,您需要的不仅仅是类Monoidal
。这已经在几个图书馆中进行了尝试。我也写过一篇。但是,呃……看起来不太漂亮……
class (Functor f r t, Cartesian r, Cartesian t) => Monoidal f r t where\n pureUnit :: UnitObject t `t` f (UnitObject r)\n fzipWith :: (ObjectPair r a b, Object r c, ObjectPair t (f a) (f b), Object t (f c))\n => r (a, b) c -> t (f a, f b) (f c)\n\nclass (Monoidal f r t, Curry r, Curry t) => Applicative f r t where\n -- ^ Note that this tends to make little sense for non-endofunctors. \n -- Consider using \'constPure\' instead.\n pure :: (Object r a, Object t (f a)) => a `t` f a \n\n (<*>) :: ( ObjectMorphism r a b\n , ObjectMorphism t (f a) (f b), Object t (t (f a) (f b))\n , ObjectPair r (r a b) a, ObjectPair t (f (r a b)) (f a)\n , Object r a, Object r b )\n => f (r a b) `t` t (f a) (f b)\n (<*>) = curry (fzipWith $ uncurry id)\n
Run Code Online (Sandbox Code Playgroud)\n\n我不知道这些框架是否允许您以实用的方式编写您想要的应用 JS 代码。有可能,但实际上我怀疑它会变得非常混乱。另请注意,虽然您现在有应用程序,但这并不意味着您实际上可以在 Js 代码中执行通常使用Hask应用程序完成的操作 - 相反,您实际上需要将这些应用程序包装为变压器周边Js
类型。
您可能需要考虑完全放弃“JS 值”,而只使用类别/箭头范例来编写。即,扭转定义:
\n\ndata JsFunc a b where\n litInt :: Integer -> JsFunc () Integer\n litString :: String -> JsFunc () String\n var :: String -> JsFunc () a\n lam :: String -> JsFunc () b -> JsFunc a b\n let :: String -> JsFunc () a -> JsFunc () b -> JsFunc () b\n ap :: JsFunc a b -> JsFunc () a -> JsFunc () b\n\ntype Js = JsFunc ()\n
Run Code Online (Sandbox Code Playgroud)\n\n在这里,我们使用()
作为类别的终端对象JsFunc
——这使得Js a \xe2\x89\xa1 JsFunc () a
箭头相当于我们通常所说的值(至少如果我们不考虑严格语义的话)。如果你走这条路,几乎所有东西都需要无点地编写,但是有语法可以假装不然,你可以很好地合并函子甚至单子(如 Kleisli 箭头)。