haskell - 如何为函子制作点(组成)和美元(应用程序)符号?
问题描述
我为函子 (○) 做了点号,但我的应用程序 (↯) 不起作用,我在test3
函数声明中有错误
{-# LANGUAGE TypeOperators #-}
module Main where
import Protolude
-- composition of functors, analog of .
infixr 9 ○
type (○) f g a = f (g a)
-- functor application, analog of $
infixr 0 ↯
type (↯) f a = f a
test :: [] (Maybe Int)
test = [Just 1]
test2 :: ([] ○ Maybe) Int
test2 = [Just 1]
test3 :: ([] ○ Maybe) ↯ Int -- error here
test3 = [Just 1]
main :: IO ()
main = do
print test
print test2
return ()
我有一个错误
[Error]• The type synonym ‘○’ should have 3 arguments, but has been given 2 • In the type signature: test3 :: ([] ○ Maybe) ↯ Int
怎么了?
更新
这是使用 newtype 的实现,因为type synonyms cannot be partially applied
(@M.Aroosi)
我不喜欢它,因为我必须一直用数据类型构造函数包装数据
有没有一种方法可以实现它而无需使用Composition
或Apply
一直包装数据?
{-# LANGUAGE TypeOperators #-}
module Main where
import Protolude
-- I can't use `type` here, because type synonyms cannot be partially applied
-- composition of functors, analog of .
infixr 9 ○
newtype (○) f g a = Composition (f (g a)) deriving (Show)
-- functor application, analog of $
infixr 0 ↯
newtype (↯) f a = Apply (f a) deriving (Show)
test :: [] (Maybe Int)
test = [Just 1]
test2 :: ([] ○ Maybe) Int
test2 = Composition [Just 1]
test2' :: [] ○ Maybe ↯ Int
test2' = Apply (Composition [Just 1])
test3 :: ([] ○ Maybe ○ Maybe) Int
test3 = Composition [Composition (Just (Just 1))]
test3' :: [] ○ Maybe ○ Maybe ↯ Int
test3' = Apply (Composition [Composition (Just (Just 1))])
main :: IO ()
main = do
print test
print test2
print test2'
print test3
print test3'
return ()
更新
这可以在 idris 中轻松完成
module Main
test : List (Maybe Integer)
test = [Just 1]
-- using (.) from prelude
test1 : (List . Maybe) Integer
test1 = [Just 1]
-- using (.) and ($) from prelude
test2 : List . Maybe $ Integer
test2 = [Just 1]
main : IO ()
main = do
print test
print test1
print test2
更新
组合type
也适用于纯脚本(耶!)
module Main where
import Prelude
import Data.Maybe (Maybe(..))
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Console (CONSOLE, logShow)
type Composition f g a = f (g a)
infixr 9 type Composition as ○
type Apply f a = f a
infixr 0 type Apply as ↯
test1 :: (Array ○ Maybe) Int
test1 = [Just 1]
test2 :: Array ○ Maybe ↯ Int
test2 = [Just 1]
test3 :: (Array ○ Maybe ○ Maybe) Int
test3 = [Just (Just 1)]
test4 :: Array ○ Maybe ○ Maybe ↯ Int
test4 = [Just (Just 1)]
main :: forall e. Eff (console :: CONSOLE | e) Unit
main = do
logShow test1
logShow test2
logShow test3
logShow test4
更新
haskell 正在努力使这成为可能
解决方案
因此,根据您的要求,这是涉及类型族的解决方案。它基于包背后的想法,并在此处Fcf
解释该想法的文章
在我开始之前,有一些东西支持使用普通数据类型/新类型:您可以为组合类型定义函子实例,使其充当一个单元,也就是说,您可以定义instance (Functor f, Functor g) => Functor (Compose f g) where ..
以下方法无法做到的。
可能有一个库允许您使用类型列表而不是仅 2 个(类似Compose [Maybe, [], Either Int] a
)来执行此操作,但我现在似乎找不到它,所以如果有人知道它可能是比我在下面介绍的一个(在我看来)。
首先我们需要一些语言扩展:
{-# LANGUAGE
TypeFamilies,
TypeInType,
TypeOperators
#-}
让我们也Data.Kind
包括Type
import Data.Kind (Type)
让我们定义一个Exp a
代表a
.
我们还将定义一个类型族Eval
,它可以让我们完成繁重的工作,它需要一个Exp a
并给我们一个a
type Exp a = a -> Type
type family Eval (e :: Exp a) :: a
我们现在可以定义我们的运算符(○)
and (↯)
(我更喜欢在这里使用更容易键入的运算符,比如 # 和 $,但我会坚持使用你为这个答案选择的那些)。
我们将这些定义为空数据类型。这就是TypeInType
进来的地方(TypeOperators
但那是因为我们正在使用运算符)。
infixr 9 ○
data (○) :: (b -> c) -> (a -> Exp b) -> a -> Exp c
infixr 0 ↯
data (↯) :: (a -> Exp b) -> a -> Exp b
注意最后一种Exp a
对他们来说是怎样的?这允许我们给他们类型实例Eval
type instance Eval ((○) f g a) = f (Eval (g a))
type instance Eval ((↯) f a) = Eval (f a)
现在你可能想知道 "(○)
的第二个参数是 kind a -> Exp b
,但我想给它类似Maybe
which has kind * -> *
!",这就是我们对这个问题有 3 个解决方案的地方:
- 添加另一个运算符,说
(%)
which 就像(○)
但接受第二个 kind 参数a -> b
而不是a -> Exp b
. 这只需要替换最右边的合成运算符。 - 将类型“提升”
a -> b
为a -> Exp b
,我将使用为此命名的数据类型Lift
。这只需要对组合中最右边的类型进行。 - 提供一种“什么都不做”的数据类型
a -> Exp b
,我称之为Pure
.
以下是用 Haskell 编写的三个解决方案:
infixr 9 %
data (%) :: (b -> c) -> (a -> b) -> a -> Exp c
type instance Eval ((%) f g a) = f (g a)
data Lift :: (a -> b) -> a -> Exp b
type instance Eval (Lift f a) = f a
data Pure :: a -> Exp a
type instance Eval (Pure a) = a
我们可以用这个设置做的另一件事是创建一个类型级别的函数数据类型,我们称之为“Compose”,它将获取一个类型列表并生成它们的组合
data Compose :: [a -> a] -> a -> Exp a
type instance Eval (Compose '[] a) = a
type instance Eval (Compose (x:xs) a) = x (Eval (Compose xs a))
现在我们可以制作我们的程序,其中包含一些测试和main
仅打印测试值的 a:
{-# LANGUAGE
TypeFamilies,
TypeInType,
TypeOperators
#-}
module Main where
import Data.Kind (Type)
type Exp a = a -> Type
type family Eval (e :: Exp a) :: a
infixr 9 ○
data (○) :: (b -> c) -> (a -> Exp b) -> a -> Exp c
infixr 0 ↯
data (↯) :: (a -> Exp b) -> a -> Exp b
type instance Eval ((○) f g a) = f (Eval (g a))
type instance Eval ((↯) f a) = Eval (f a)
infixr 9 %
data (%) :: (b -> c) -> (a -> b) -> a -> Exp c
type instance Eval ((%) f g a) = f (g a)
data Lift :: (a -> b) -> a -> Exp b
type instance Eval (Lift f a) = f a
data Pure :: a -> Exp a
type instance Eval (Pure a) = a
data Compose :: [a -> a] -> a -> Exp a
type instance Eval (Compose '[] a) = a
type instance Eval (Compose (x:xs) a) = x (Eval (Compose xs a))
test :: [] (Maybe Int)
test = [Just 1]
-- using %
test2 :: Eval (([] % Maybe) Int)
test2 = [Just 1]
test2' :: Eval ([] % Maybe ↯ Int)
test2' = [Just 1]
-- works for longer types too
test3 :: Eval (([] ○ Maybe % Maybe) Int)
test3 = [Just (Just 1)]
test3' :: Eval ([] ○ Maybe % Maybe ↯ Int)
test3' = [Just (Just 1)]
-- we can instead Lift the rightmost type
test4 :: Eval (([] ○ Maybe ○ Lift Maybe) Int)
test4 = [Just (Just 1)]
test4' :: Eval ([] ○ Maybe ○ Lift Maybe ↯ Int)
test4' = [Just (Just 1)]
-- an even longer type, with definition "matching" the type declaration
test5 :: Eval ([] ○ Maybe ○ Either Bool % Maybe ↯ Int)
test5 = (:[]) . Just . Right . Just $ 1
-- Same as above, but instead let's use Pure so we don't need to lift the Maybe or use %
test6 :: Eval ([] ○ Maybe ○ Either Bool ○ Maybe ○ Pure ↯ Int)
test6= (:[]) . Just . Right . Just $ 1
-- same as above, uses Compose
test7 :: Eval (Compose [[], Maybe, Either Bool, Maybe] Int)
test7= (:[]) . Just . Right . Just $ 1
main :: IO ()
main = do
print test
print test2
print test2'
print test3
print test3'
print test4
print test4'
print test5
print test6
print test7
return ()
推荐阅读
- php - 数组中的元素不适用于 in_array
- c# - 如何使用 c# 在 Unity 中移动到下一个级别/场景
- reactjs - 动态文件源 React-PDF (Hooks)
- python - 尝试求解 x 时获取“int”对象不可迭代
- c++ - 当我通过代码arduino esp32传递命令时,sim800 cme erro2
- node.js - 如何解决 mongoddb adbpter 查询构建问题中的 $near 查询?
- node.js - nodejs writeFile 仅适用于管理员权限
- r - R通过将事物组合在一起制作百分比的pi图
- python - 在 EventGhost 中使用 Python
- python - 获取网站部分的 id 或 xpath