是否可以通过以下方式"分配"并在Haskell中传递值:
foo :: Int -> Int -> Int
foo x y = x + y
bar :: ??
bar = 2 3 -- ??
foo bar == 5
Run Code Online (Sandbox Code Playgroud)
Foo是一个需要两个整数的函数.我想将这些int分配给一个bar并将该bar传递给foo,以便它可以产生它的结果.我想实现这一点,而不使用像这样的元组,新类型或值分离:
bar1 = 2
bar2 = 3
foo bar1 bar2 == 5
Run Code Online (Sandbox Code Playgroud)
这个问题出于好奇而没有任何实际原因.
有一个明智的答案:
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
Run Code Online (Sandbox Code Playgroud)
有一个古怪的答案:
bar :: (A -> B -> c) -> c
bar f = f A B
bar foo :: C
Run Code Online (Sandbox Code Playgroud)
这是作弊的答案:
{-# 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))
Run Code Online (Sandbox Code Playgroud)
有依赖的答案(这只是一个提升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
Run Code Online (Sandbox Code Playgroud)
有一个精神错乱的答案:
{-# 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
Run Code Online (Sandbox Code Playgroud)
有真正的答案:
不.简单而完全的事实是它的类型foo有两个(->),这意味着你需要两个函数应用程序来完全使用它.有各种技巧(按顺序,uncurry继续传递风格,超出语言规避其规则,时髦的伪依赖类型"有趣的东西"和重载foo)将这种负担转移到其他地方(相应uncurry的定义,在bar我的定义中,在扩展的CPP后代码中appHList,在其中一个实例中FooType)但它们都没有真正实现你想要的.(CPS是倒退的,众所周知难以阅读,CPP刚刚被破坏,类型类的东西需要大量修改foo(大多数语言扩展只是为了效果;起诉我),uncurry并且其更高级的堂兄appHList需要额外的函数调用).