哈斯克尔| 为事物分配多个值,以便将它们作为单个参数传递给函数

Pet*_*lys 3 haskell

是否可以通过以下方式"分配"并在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)

这个问题出于好奇而没有任何实际原因.

HTN*_*TNW 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
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需要额外的函数调用).