在一个我一直在纠缠的学习问题中,我意识到我需要一个具有用于应用,编写等操作的函数的类型类。原因...
将函数的表示形式视为函数本身就是方便的,因此隐式应用函数将使用解释器,而组成函数会得出新的描述。
一旦有了函数的类型类,就可以为特殊类型的函数派生类型类-就我而言,我想要可逆函数。
例如,应用整数偏移量的函数可以由包含整数的ADT表示。应用这些功能仅意味着将整数相加。通过添加包装的整数来实现合成。逆函数的整数取反。身份函数包装零。无法提供常量函数,因为没有合适的表示形式。
当然,它不需要像值是真正的Haskell函数那样拼写事物,但是一旦我有了主意,我认为这样的库必须已经存在,甚至可以使用标准拼写。但是我在Haskell库中找不到这样的类型类。
我找到了Data.Function模块,但是没有类型类-只有一些常见的函数也可以从Prelude中获得。
那么-为什么没有函数的类型类?是“仅仅是因为没有”还是“因为它没有您想象的那么有用”?还是这个想法存在根本性的问题?
到目前为止,我想到的最大可能的问题是,实际功能上的功能应用程序可能必须由编译器进行特殊处理,以避免出现循环问题-为了应用此功能,我需要应用功能应用程序功能,为此,我需要调用函数应用程序函数,然后执行此操作...
更多线索
示例代码显示我的目标...
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
-- In my first version, Doable only had the one argument f. This version
-- seemed to be needed to support the UndoableOffset type.
--
-- It seems to work, but it also seems strange. In particular,
-- the composition function - a and b are in the class, but c isn't,
-- yet there's nothing special about c compared with a and b.
class Doable f a b where
fwdApply :: f a b -> a -> b
compDoable :: f b c -> f a b -> f a c
-- In the first version, I only needed a constraint for
-- Doable f a b, but either version makes sense.
class (Doable f a b, Doable f b a) => Undoable f a b where
bwd :: f a b -> f b a
bwdApply :: f a b -> b -> a
bwdApply f b = fwdApply (bwd f) b
-- Original ADT - just making sure I could wrap a pair of functions
-- and there were no really daft mistakes.
data UndoableFn a b = UFN { getFwd :: a -> b, getBwd :: b -> a }
instance Doable UndoableFn a b where
fwdApply = getFwd
compDoable f g = UFN ((getFwd f) . (getFwd g)) ((getBwd g) . (getBwd f))
instance Undoable UndoableFn a b where
bwd f = UFN (getBwd f) (getFwd f)
bwdApply = getBwd
-- Making this one work led to all the extensions. This representation
-- can only represent certain functions. I seem to need the typeclass
-- arguments, but also to need to restrict which cases can happen, hence
-- the GADT. A GADT with only one constructor still seems odd. Perhaps
-- surprisingly, this type isn't just a toy (except that the whole thing's
-- a toy really) - it's one real case I need for the exercise. Still a
-- simple special case though.
data UndoableOffset a b where
UOFF :: Int -> UndoableOffset Int Int
instance Doable UndoableOffset Int Int where
fwdApply (UOFF x) y = y+x
compDoable (UOFF x) (UOFF y) = UOFF (x+y)
instance Undoable UndoableOffset Int Int where
bwdApply (UOFF x) y = y-x
bwd (UOFF x) = UOFF (-x)
-- Some value-constructing functions
-- (-x) isn't shorthand for subtraction - whoops.
undoableAdd :: Int -> UndoableFn Int Int
undoableAdd x = UFN (+x) (\y -> y-x)
undoableMul :: Int -> UndoableFn Int Int
undoableMul x = UFN (*x) (`div` x)
-- With UndoableFn, it's possible to define an invertible function
-- that isn't invertible - to break the laws. To prevent that, need
-- the UFN constructor to be private (and all public ops to preserve
-- the laws). undoableMul is already not always invertible.
validate :: Undoable f a b => Eq a => f a b -> a -> Bool
validate f x = (bwdApply f (fwdApply f x)) == x
-- Validating a multiply-by-zero invertible function shows the flaw
-- in the validate-function plan. Must try harder.
main = do putStrLn . show $ validate (undoableAdd 3) 5
putStrLn . show $ validate (undoableMul 3) 5
--putStrLn . show $ validate (undoableMul 0) 5
fb1 <- return $ UOFF 5
fb2 <- return $ UOFF 7
fb3 <- return $ compDoable fb1 fb2
putStrLn $ "fwdApply fb1 3 = " ++ (show $ fwdApply fb1 3)
putStrLn $ "bwdApply fb1 8 = " ++ (show $ bwdApply fb1 8)
putStrLn $ "fwdApply fb3 2 = " ++ (show $ fwdApply fb3 2)
putStrLn $ "bwdApply fb3 14 = " ++ (show $ bwdApply fb3 14)
该应用程序涉及一种统一形式,其中统一值不相等,但是通过可逆函数(Prolog样式的逻辑,但具有a = f(b)
约束而不是)相互关联a = b
。大部分成分将来自优化联合查找结构。逆的需求应该很明显。
如果统一集中的任何项目都不具有确切值,则只能相对于该统一集中的另一个项目对特定项目进行量化。这就是为什么我不想使用“真实”函数-计算那些相对值的原因。我可以放弃整个函数方面,只包含绝对数量和相对数量-我可能只需要数字/向量,并且(+)
-但我的内部架构宇航员想要他的乐趣。
我再次断开链接的唯一方法是通过回溯,并且一切都是纯的-联合查找将使用键中的IntMap
“指针”完成。我已经完成了简单的联合查找工作,但是由于我还没有添加可逆函数,因此这里没有列出它。
我无法使用Applicative,Monad,Arrow等的原因
我需要函数抽象类提供的主要操作是应用程序和组合。听起来很熟悉-例如Applicative
(<*>)
,Monad
(>>=)
和Arrow
(>>>)
都是合成功能。但是,在我的案例中,实现函数抽象的类型将包含一些表示函数的数据结构,但该数据结构不是(也不能包含)函数,并且只能表示某些有限的函数集。
正如我在代码说明中提到的那样,有时我只能量化一个项目相对于另一个项目,因为“统一”群集中没有任何项目具有确切的值。我希望能够导出该函数的表示形式,通常是由几个提供的函数(在并集/查找树中向上移动到一个共同祖先)和几个逆函数(向后向下移动到另一个)的组成项目)。
简单的情况-原始的“函数”仅限于整数偏移量“函数”,我希望将组合结果作为整数偏移量“函数”-添加组件偏移量。这就是为什么组合功能和应用程序功能都必须在类中的重要原因。
这意味着我不能提供的业务pure
,return
或者arr
对我的类型,所以我不能使用Applicative
,Monad
或者Arrow
。
这不是那些类型的失败-这是抽象的不匹配。我想要的抽象是一个简单的纯函数。例如,它没有副作用,也不需要为对所有函数进行等效的标准(。)等效构建用于对函数进行排序和组成的便捷符号。
我可以举例Category
。我有信心,尽管我可能不需要,但我的所有功能都能提供一个身份。但是由于Category
不支持应用程序,因此无论如何我仍然需要一个派生类来添加该操作。
Applicative
是不对的-它要求将值和函数都包装在一起,而我只想包装函数,而包装的函数实际上就是函数,而包装的函数通常不会(在在最一般的情况下,它们是描述函数的AST)。where <*>
具有type f (a -> b) -> f a -> f b
,我想要一个类型为g a b -> a -> b
where 的应用程序运算符,a
并b
指定包装函数的域和共域,但是包装器内部不是(不一定)真正的函数。在Arrows上-可能,我来看一下。