假设我有一个像.item
{
position: relative;
display: inline-block;
width: 20%;
height: 100px;
background-color: green;
margin-right: 10px;
transition: all 0.4s ease;
}
.item:hover
{
transform: scale(1.5);
margin-right: 6%; /* fallback for ancient browsers that don't support calc() */
margin-right: calc(5% + 10px);
margin-left: 5%;
}
这样的元组。只是为了好玩(阅读:学习),我想创建一个函数,将一些正确形式的函数应用于任何这样的元组并返回结果。用法示例:
('a',(1,("Hello",False))
我完成了大部分内容如下:
applyFnToTuple ('o',('t','w')) $ \a b c -> [a,b,c] == "otw"
applyFnToTuple ('h','i') $ \a b -> [a,b] == "hi"
applyFnToTuple ("hello",('y','o')) $ \a b c -> a ++ [b,c]
关键点是最后一例。我完全期望需要添加type family TupleFn ty out where
TupleFn (a,b) output = a -> (TupleFn b output)
TupleFn b output = b -> output
class ApplyFnToTuple a where
applyFnToTuple :: a -> TupleFn a out -> out
instance ApplyFnToTuple b => ApplyFnToTuple (a,b) where
applyFnToTuple (a,b) fn = applyFnToTuple b (fn a)
instance ApplyFnToTuple a where
applyFnToTuple b fn = fn b
,因为{-# OVERLAPPABLE #-}
比a
更通用。我也很难确切地看到GHC如何解决(a,b)
以及a
类的正确版本并知道正确的类型sig,但我可以很容易地将其归结为我自己缺乏理解。但无论如何,GHCI给我的实际错误是:
TupleFn
据我所知,我的TupleFn没有版本返回没有参数的东西,所以我真的不明白错误。但是,我发现只需将最后一个实例更改为更具体的例子即可编译:
Couldn't match expected type ‘a -> out’
with actual type ‘TupleFn a out’
Relevant bindings include
fn :: TupleFn a out (bound at examples.hs:574:22)
b :: a (bound at examples.hs:574:20)
applyFnToTuple :: a -> TupleFn a out -> out
(bound at examples.hs:574:5)
The function ‘fn’ is applied to one argument,
but its type ‘TupleFn a out’ has none
In the expression: fn b
In an equation for ‘applyFnToTuple’: applyFnToTuple b fn = fn b
Failed, modules loaded: none.
但这意味着我必须定义许多类似的实例,这是不可取的。
我想知道的是,是否有一种相对简单的方法可以使更通用的版本工作,以及为什么会出现这种特殊错误?
谢谢你:)
PS:我正在运行GHC 7.10.1
答案 0 :(得分:6)
问题是在的instance ApplyFnToTuple a
定义中,无法访问a
不是元组的信息 - 我猜GHC没有考虑如何选择实例来决定它是否作为定义是正确的。这意味着它无法知道TupleFn
给出了正确的结果,因此实例不会进行类型检查。
要解决此问题,您可以向告诉添加一个等式约束TupleFn
是正确的。不幸的是,由于约束必须提到out
类型,因此需要将其作为类的额外类型参数包含在内。至少,以下似乎有效(仅使用GHC 7.8进行测试):
{-# LANGUAGE TypeFamilies, FlexibleInstances,
MultiParamTypeClasses,
OverlappingInstances #-}
type family TupleFn ty out where
TupleFn (a,b) output = a -> (TupleFn b output)
TupleFn b output = b -> output
class ApplyFnToTuple a out where
applyFnToTuple :: a -> TupleFn a out -> out
instance ApplyFnToTuple b out => ApplyFnToTuple (a,b) out where
applyFnToTuple (a,b) fn = applyFnToTuple b (fn a)
instance TupleFn a out ~ (a -> out) => ApplyFnToTuple a out where
applyFnToTuple b fn = fn b
答案 1 :(得分:3)
像往常一样,你可以用单身人士和打字家庭来做这件事:
{-# LANGUAGE GADTs, DataKinds, TypeFamilies, TypeOperators #-}
type family Tuple b as where
Tuple b '[] = b
Tuple b (a ': as) = (b, Tuple a as)
type family Function as b where
Function '[] b = b
Function (a ': as) b = a -> Function as b
data SingList as where
SNil :: SingList '[]
SCons :: SingList as -> SingList (a ': as)
applyToTuple :: SingList as -> Tuple a as -> Function (a ': as) b -> b
applyToTuple SNil x f = f x
applyToTuple (SCons as) (x, xs) f = applyToTuple as xs (f x)
main = do
print $ applyToTuple (SCons (SCons SNil)) ('o',('t','w')) $ \a b c -> [a,b,c] == "otw"
print $ applyToTuple (SCons SNil) ('h','i') $ \a b -> [a,b] == "hi"
print $ applyToTuple (SCons (SCons SNil)) ("hello",('y','o')) $ \a b c -> a ++ [b,c]
Tuple a [b, c, d]
缩减为(a, (b, (c, d)))
。
Function [a, b, c, d] r
缩减为a -> b -> c -> d -> r
。
因此,如果as == [b, c, d]
,那么
Tuple a as -> Function (a ': as) r -> r
缩减为
(a, (b, (c, d))) -> (a -> b -> c -> d -> r) -> r
答案 2 :(得分:2)
正如DanielWagner建议的那样,我最后更喜欢略微调整的格式(在元组链的末尾使用()来表示完成)。这使得它非常简单:
type family TupleFn ty out where
TupleFn () output = output
TupleFn (a,b) output = a -> (TupleFn b output)
class ApplyFnToTuple a where
applyFnToTuple :: a -> TupleFn a out -> out
instance ApplyFnToTuple b => ApplyFnToTuple (a,b) where
applyFnToTuple (a,b) fn = applyFnToTuple b (fn a)
instance ApplyFnToTuple () where
applyFnToTuple _ fn = fn
这可以像:
一样使用applyFnToTuple ('a',('b',())) $ \a b -> [a,b] == "ab"
applyFnToTuple ("hello",(12,('r',()))) $ \h n r -> h ++ show n ++ [r] == "hello12r"
我希望可以调整实例;这些只是我尝试GHC喜欢的第一个:)
ØrjanJohansen的方法(见他的答案)有点复杂,但提供了更整洁的最终案例!
作为旁注,我想将一些结构翻译成相应的函数,我实际上最终只是使用我自己的数据类型来获得额外的功能。我可以提出的最简单的形式(现在不使用DataKinds)作为例子:
--using DataKinds these could be done slightly neater:
data Cons a b
data Nil
-- the list itself, where the type 'a' is built from the above tags
data MyList a where
LCons :: itemty -> MyList a -> MyList (Cons itemty a)
LNil :: MyList Nil
-- this type family converts that type 'a' to a function signature.
type family MyListFn a output where
MyListFn (Cons a b) output = a -> (MyListFn b output)
MyListFn Nil output = output
-- this function applies items in MyList a to a MyListFn a just
-- like we did with tuples. Note no type family, because
-- no type dependant differences in behaviour needed:
applyFnToMyList :: MyList a -> MyListFn a out -> out
applyFnToMyList (LCons a b) fn = applyFnToMyList b (fn a)
applyFnToMyList LNil fn = fn
与元组大小写的用法非常相似:
applyFnToMyList (LCons 'a' (LCons 'b' LNil)) $ \a b -> [a,b] == "ab"
applyFnToMyList (LCons "hello" (LCons 12 (LCons 'r' LNil))) $ \h n r -> h ++ show n ++ [r] == "hello12r"
TL; DR 您可以创建函数,以完全类型安全的方式应用多态数据结构的某些元素所需的奇偶校验函数。很棒的东西,Haskell!