可以在Haskell或其他函数式语言中正式定义以下类型T
吗?
类型T
包含一些函数,给定来自X
的集合(即一组)对象,为该集合中的每个对象分配一个数字
例如,假设我们有一个来自t
的函数T
。它的参数arg
必须是来自X
的对象的集合(集合),例如字符串列表['abc','def','xyz']。它的返回值必须是函数r
,它只需要三个可能的参数:'abc'
,'def'
或'xyz'
,并返回一个数字。在这种情况下,我特别不希望r
只接受任何字符串作为参数。
一个例子可能是“等级函数”,给定一组对象,它们以某种方式为每个对象分配一个等级。它不仅仅返回一组数字;相反,它返回一个函数,该函数接受原始集合的任何成员,并返回该成员的等级。
当然,如果我稍微改变一下我的要求,事情会变得非常简单。我可以问函数t
只需要一组对象并返回一组数字。这几乎是,但不完全相同。这样的定义需要一些额外的工作:我必须将输入集合中的对象与输出集合中的对象进行匹配。并且它也不会那么精确:可能返回的数字集合可能与输入对象不匹配(例如,可能有一个数字太多)。
如果我所描述的约束不能表达为类型约束,我不会感到惊讶,并且应该以不同的方式强制执行。
编辑:我最初还要求定义一个类型U
,其中包含将X
中的函数转换为数字的函数,并返回类型为T
的函数。但我没有很好地解释这一点,它只会增加我的问题的混乱。所以最好忽略我问题的这一部分。
答案 0 :(得分:1)
编辑:根据问题的更新,我完全重写了这个答案。
忽略对第二种类型的强调并采取实际方法,让我们在Haskell中构建此功能。首先,这是一个函数,给定一个函数,一个对象列表和一个对象,如果该对象在列表中,则将该函数应用于该对象。
u f list x = assert (x `elem` list) $ f x
(断言函数来自Control.Exception
)这也可以通过后卫来实现:
u f list x | (x `elem` list) = f x --similar but gives a slightly different error msg
在ghci中运行一些示例给出了以下内容:
*Main> u (+1) [1,2,3] 1
2
*Main> u (+1) [1,2,3] 2
3
*Main> u (+1) [1,2,3] 3
4
*Main> u (+1) [1,2,3] 4
*** Exception: test.hs:3:14-19: Assertion failed
然而,这不是非常安全的代码,因为没有静态保证不会以这种方式调用该函数导致崩溃。
另一种方法是使用Maybe
类型。
u' f list x = if elem x list then Just (f x) else Nothing
具有以下行为:
*Main> u' (+1) [1,2,3] 2
Just 3
*Main> u' (+1) [1,2,3] 3
Just 4
*Main> u' (+1) [1,2,3] 4
Nothing
这仍然不能保证不会以不正确的方式调用该函数,但它确实静态地保证基于返回值的任何结果必须同时处理Just result
和Nothing
。这也具有Maybe
是monad的优点,可以将其视为可能失败的计算。所以你可以像这样轻松地链接电话
*Main> let func = u' (+1) [1,2,3]
*Main> func 1
Just 2
*Main> func 1 >>= func
Just 3
*Main> func 1 >>= func >>= func
Just 4
*Main> func 1 >>= func >>= func >>= func
Nothing
这似乎是在Haskell中解决这类问题的最佳实用方法。但是,问题是询问是否有一个类型专门固定这样的函数。让我们看看这些函数的类型:
u :: Eq x => (x -> a) -> [x] -> x -> a
u' :: Eq x => (x -> a) -> [x] -> x -> Maybe a
这些类型都没有要求它是列表的成员资格,使它们工作与否。我认为将其作为一种类型的唯一方法是将集合本身视为一种类型。调用col
类型,然后我们就可以
u :: (col -> x) -> col -> x
但这不仅具有很小的实用价值,而且还没有精确地确定这些功能。基于对的方法也不是。根据我的经验,尝试构建高度限制性类型(例如仅包含['abc', 'def', 'xyz']
中的值的类型)并不是非常实用。
答案 1 :(得分:1)
好的,我想重命名你的类型以帮助理解它们。 T
为数字指定数字,因为我不确定容器的结构是什么(并希望保持灵活性),我会在一对(x,n)
中弹出值和数字。我们称之为标记,因此重新命名为T
Tagger
,因此假设我知道您的集合类型为Coll
,X
的集合将具有Coll X
类型,因此我需要
type CollTaggerXInt = Coll X -> Coll (X,Int)
,它为您想要的函数提供类型同义词。
但是如果Int
太小而您想使用Integer
,Double
或其他数字类型会怎么样?
data Num n => CollTaggerX n = CollTaggerX (Coll X -> Coll (X,n))
这意味着您可以使用任何固定类型的数字数据标记X
值。 (Num n =>
是一种数据类型约束,断言n必须是数字类型。)右侧的CollTaggerX通过将标记函数包装在轻量级构造函数中来确保类型安全。我们需要使用data
代替type
,因为我已按n
参数化。
我倾向于用类型参数替换固定类型X
和集合类型Coll
(比如某些语言中的泛型,例如Java),以改善代码重用:
data (Functor coll,Num n) => Tagger coll x n = Tgr (coll x -> coll (x,n))
所以现在我们坚持认为coll
类型是Functor,所以我们可以使用fmap
将函数逐点应用于集合(在您的情况下至关重要,任何集合类型都是Functor的一个例子。
我对Tagger
T
的{{1}}定义感到最满意,但如果CollTaggerXInt
coll
只有一种可能性,则可以使用x
n
}和U
。
您的Lift
类型用于将元素标记符转换为集合标记符。我想称之为U
,而不是CollTaggerXInt
。如果您使用的是type LiftXIntToTagger = (X -> Int) -> Coll X -> Coll (X,Int)
,则可以再次使用类型同义词:
Tagger
或者如果您使用更灵活的data (Functor coll,Num n) => Lifter coll x n = Lifter ((x -> n) -> coll x -> coll (x,n))
定义,则可以编写
fmap
但是这对于为此制作类型感到疯狂,因为如果你有一个逐点函数,你可以使用coll
提升它,它仍适用于fmap :: Functor f => (a -> b) -> f a -> f b
类型:
coll
因此,我们可以将f
用作x
,将a
用作(x,n)
,将liftT :: (Functor coll,Num n) => (x -> n) -> coll x -> coll (x,n)
liftT f = fmap tag where
tag x = (x,f x)
用作b,并定义
liftT
如果您想定义类型,请确定,但我认为U
可能是您的类型rankfunction :: coll x -> x -> n
中唯一明智的功能。
现在我认为你的排名示例很有用,所以让我们来研究一下。一个rankfunction需要检查集合中的所有元素,所以让我们给它整个集合作为它的第一个参数,所以(Functor coll,Num n)
(在上下文liftInContext :: (Functor coll,Num n) => (coll x -> x -> n) -> coll x -> coll (x,n)
liftInContext rankfunction mycoll = liftT (rankfunction mycoll) mycoll
中)。
(rankfunction mycoll)
在使用liftT将其应用于每个元素之前,函数{{1}}将rankfunction传递给它的第一个参数 - 整个集合。这称为部分评估,对于这类事情非常方便。
答案 2 :(得分:0)
如果你想要一个获取值并返回一个类型的函数(除其他外),你将需要依赖类型。你可以在Haskell中simulate dependent types,但它并不漂亮。