是否可以通过以下方式“分配”并在Haskell中传递值:
foo :: Int -> Int -> Int
foo x y = x + y
bar :: ??
bar = 2 3 -- ??
foo bar == 5
Foo是一个需要两个整数的函数。我想将这些int分配给一个bar并将该bar传递给foo,以便它可以产生它的结果。我想在不使用元组,新类型或值分离的情况下实现这一点:
bar1 = 2
bar2 = 3
foo bar1 bar2 == 5
这个问题出于好奇而没有任何实际原因。
答案 0 :(得分:5)
有一个明智的答案:
Data.Tuple.uncurry :: (a -> b -> c) -> (a, b) -> c
data A; data B; data C
foo :: A -> B -> C
bar :: (A, B)
uncurry foo bar :: C
有一个古怪的答案:
bar :: (A -> B -> c) -> c
bar f = f A B
bar foo :: C
有作弊的答案:
{-# LANGUAGE CPP #-}
#define bar A B
foo bar :: C
-- expansion: foo A B :: C
-- Don't try to use this in any other way!
-- (bar, 2) ~> (A B, 2) -> error
-- It's incredibly unhygienic and WILL explode at some point
-- You probably want to uppercase the name to make it stick out
-- Or maybe
#undef bar
#define AP2(x) (fst (x)) (snd (x))
bar :: (A, B)
foo AP2(bar) :: C
#define BAR AP2(bar)
foo BAR :: C
-- expansion: foo (fst (A, B)) (snd (A, B))
有依赖性答案(只是提升uncurry
):
{-# LANGUAGE DataKinds, FlexibleInstances, FlexibleContexts, FunctionalDependencies, GADTs, MultiParamTypeClasses, TypeOperators, UndecidableInstances #-}
data HList (xs :: [*]) where
Hil :: HList '[]
Hons :: x -> HList xs -> HList (x : xs)
class AppHList f (a :: [*]) r | f a -> r where
appHList :: f -> HList a -> r
instance AppHList x '[] x where
appHList x Hil = x
instance (f ~ (a -> b), a ~ x, AppHList b xs r) => AppHList f (x : xs) r where
appHList f (Hons x xs) = appHList (f x) xs
-- if it's not obvious, HLists of two elements are isomorphic to 2-tuples
-- toT (Hons a (Hons b Hil)) = (a, b)
-- toH (a, b) = (Hons a (Hons b Hil))
-- toT . toH = id = toH . toT
-- and we get
-- uncurry = (. toH) . appHList
-- so, on some level, they are the same
bar :: HList [A, B]
bar = Hons A$Hons B$Hil
appHList foo bar :: C
精神错乱的答案:
{-# LANGUAGE DataKinds, FlexibleInstances, GADTs, TypeOperators #-}
import GHC.TypeLits
class FooType t
foo :: t
-- GADTs is only here for the ~s, and they aren't really needed
-- They're only here to make foo (B, A) a "needed (A, B) got (B, A)"
-- instead of a "instance not found"
instance (a ~ A, b ~ B) => FooType ((a, b) -> C) where
foo (a, b) = foo a b
instance FooType (A -> B -> C) where
foo a b = _
-- This instance pulls in GHC.TypeLits, DataKinds, TypeOperators, and UndecidableInstances
-- all in the name of pretty error messages.
instance {-# OVERLAPPABLE #-} TypeError (Text "foo is a function from "
:<>: ShowType A
:<>: Text " and "
:<>: ShowType B
:<>: Text " to "
:<>: ShowType C
:$$: Text "It may not be given the type "
:<>: ShowType t
) => FooType t where
foo = undefined
真正的答案是:
没有。简单而彻底的事实是foo
在其类型中有两个(->)
,这意味着您需要两个函数应用程序才能完全使用它。有各种技巧(按顺序,uncurry
,继续传递风格,超出语言规避其规则,时髦的伪依赖类型&#34;有趣的东西&#34;和重载foo
)将这个负担转移到其他地方(在uncurry
的定义中,在bar
的定义中,在扩展的CPP后代码中appHList
中。 ,在FooType
的一个实例中,但没有一个真正实现你想要的。 (CPS是倒退的,众所周知难以阅读,CPP刚刚被破坏,类型类的东西需要大量修改foo
(大多数语言扩展只是为了效果;起诉我),uncurry
及其发烧友堂兄appHList
需要额外的函数调用。)
答案 1 :(得分:1)
为了让foo bar == 5
的示例有效,您需要确保类型匹配......
foo
需要两个Int
并返回Int
并且类型为foo :: Int -> (Int -> Int)
。括号不是严格必要的,但为了清楚起见,我已添加它们(->
是右关联的。)
您可以从中看到您的示例不起作用。您正在尝试将函数bar
作为第一个参数传递,它实际上期望Int
。唯一可行的方法是bar
返回Int
,在这种情况下,您只能传入一个变量。
由于上面的包围,您不能将foo
(Int -> Int)
作为参数。这是传递两个变量而没有元组或其他东西的唯一方法,但是不可能。
所以不幸的是,我认为实现这一目标的最好方法是使用元组,也可以按照建议使用uncurry
。虽然有趣的问题!