改变Haskell的编程器编程器

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的一个实例?

lef*_*out 3

嗯,首先——

\n\n
\n

...如果它使用真正的 Functors 而不仅仅是 Endofunctors ...

\n
\n\n

Haskell 函子真正的函子。只是,该类Functor不允许任何类型的通用函子,而只允许Hask上的特定函子。

\n\n

事实上,非内函子很有趣。数学家一直在使用它们。虽然正如你所说,不可能有非 endofunctor monad,但类似的情况Applicative 可能的:该类实际上只是一个很好的Monoidal functors的 Haskell 接口,它可以在不同的类别之间定义。看起来大约是这样的:

\n\n
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。这已经在几个图书馆进行了尝试。我也写过一篇。但是,呃……看起来不太漂亮……

\n\n
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类型。

\n\n

您可能需要考虑完全放弃“JS 值”,而使用类别/箭头范例来编写。即,扭转定义:

\n\n
data 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 箭头)。

\n