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

时间:2018-01-21 19:40:40

标签: haskell

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

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

2 个答案:

答案 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。虽然有趣的问题!