如何定义Lisp在Haskell中的应用?

| 难道不应该在像Haskell这样的懒惰的语言中使用该定义来定义函数吗?
apply f [] = f
apply f (x:xs) = apply (f x) xs
它基本上是一个将给定函数应用于给定参数列表的函数,例如在Lisp中非常容易做到。 有什么解决方法吗?     
已邀请:
        很难将静态类型赋予
apply
函数,因为它的类型取决于(可能是异构的)列表参数的类型。我可以想到至少有两种方法可以在Haskell中编写此函数: 使用反射 我们可以将应用程序的类型检查推迟到运行时:
import Data.Dynamic
import Data.Typeable

apply :: Dynamic -> [Dynamic] -> Dynamic
apply f []      = f
apply f (x:xs)  = apply (f `dynApp` x) xs
请注意,现在Haskell程序可能会在运行时因类型错误而失败。 通过类型类递归 使用半标准的
Text.Printf
技巧(由IIRC于augustss发明),可以将这种解决方案编码为这种样式(运动)。尽管它可能不是很有用,但仍需要一些技巧来隐藏列表中的类型。 编辑:如果不使用动态类型或hlists / existentials,我想不出一种写此方法的方法。希望看到一个例子     
        我喜欢Sjoerd Visscher的答复,但是扩展名-尤其是
IncoherentInstances
,在这种情况下用于部分应用,可能有点令人生畏。这是不需要任何扩展的解决方案。 首先,我们定义一个函数的数据类型,该函数知道如何处理任意数量的参数。您应在此处将ѭ5读为\“参数类型\”,将
b
读为\“返回类型\”。
data ListF a b = Cons b (ListF a (a -> b))
然后,我们可以编写一些(Haskell)函数来调整这些(可变)函数。对于后奏中出现的所有功能,我都使用
F
后缀。
headF :: ListF a b -> b
headF (Cons b _) = b

mapF :: (b -> c) -> ListF a b -> ListF a c
mapF f (Cons v fs) = Cons (f v) (mapF (f.) fs)

partialApply :: ListF a b -> [a] -> ListF a b
partialApply fs          []     = fs
partialApply (Cons f fs) (x:xs) = partialApply (mapF ($x) fs) xs

apply :: ListF a b -> [a] -> b
apply f xs = headF (partialApply f xs)
例如,
sum
函数可被视为可变函数:
sumF :: Num a => ListF a a
sumF = Cons 0 (mapF (+) sumF)

sumExample = apply sumF [3, 4, 5]
但是,我们还希望能够处理普通函数,这些函数不一定知道如何处理任何数量的参数。那么该怎么办?好吧,像Lisp一样,我们可以在运行时引发异常。下面,我们将使用ѭ12作为非变性函数的简单示例。
f True True True  = 32
f True True False = 67
f _ _ _ = 9

tooMany = error \"too many arguments\"
tooFew  = error \"too few arguments\"
lift0 v = Cons v tooMany
lift1 f = Cons tooFew (lift0 f)
lift2 f = Cons tooFew (lift1 f)
lift3 f = Cons tooFew (lift2 f)

fF1 = lift3 f

fExample1 = apply fF1 [True, True, True]
fExample2 = apply fF1 [True, False]
fExample3 = apply (partialApply fF1 [True, False]) [False]
当然,如果您不喜欢分别定义
lift0
lift1
lift2
lift3
等的样板,则需要启用一些扩展。但是没有它们,您可以走得很远! 这是您可以概括为单个
lift
函数的方法。首先,我们定义一些标准的类型级别数字:
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, TypeFamilies, UndecidableInstances #-}

data Z = Z
newtype S n = S n
然后介绍要提升的typeclass。您应该将类​​型
I n a b
读取为\“
a
n
副本作为参数,然后返回ѭ6of \”的返回类型。
class Lift n a b where
    type I n a b :: *
    lift :: n -> I n a b -> ListF a b

instance Lift Z a b where
    type I Z a b = b
    lift _ b = Cons b tooMany

instance (Lift n a (a -> b), I n a (a -> b) ~ (a -> I n a b)) => Lift (S n) a b where
    type I (S n) a b = a -> I n a b
    lift (S n) f = Cons tooFew (lift n f)
这是之前使用ѭ12的示例,并使用广义提升进行了重写:
fF2 = lift (S (S (S Z))) f

fExample4 = apply fF2 [True, True, True]
fExample5 = apply fF2 [True, False]
fExample6 = apply (partialApply fF2 [True, False]) [False]
    
        不,它不能。
f
f x
是不同的类型。由于haskell具有静态类型的性质,因此它无法执行任何功能。它必须具有特定类型的功能。 假设以类型
a -> b -> c
传入
f
。那么
f x
的类型为
b -> c
。但是
a -> b -> c
必须具有与
a -> b
相同的类型。因此,类型
a -> (b -> c)
的函数必须是类型
a -> b
的函数。因此
b
必须与
b -> c
相同,后者是无限类型
b -> b -> b -> ... -> c
。它不存在。 (继续用
b -> c
代替
b
)     
        这是在GHC中执行此操作的一种方法。您将在这里和那里需要一些类型注释,以使GHC确信一切都将顺利进行。
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE IncoherentInstances #-}

class Apply f a r | f -> a r where
  apply :: f -> [a] -> r
instance Apply f a r => Apply (a -> f) a r where
  apply f (a:as) = apply (f a) as
instance Apply r a r where
  apply r _ = r

test = apply ((+) :: Int -> Int -> Int) [1::Int,2]

apply\' :: (a -> a -> a) -> [a] -> a
apply\' = apply

test\' = apply\' (+) [1,2]
    
        此代码很好地说明了静态类型检查和动态类型检查之间的区别。使用静态类型检查,编译器无法确定
apply f
是否确实传递了
f
期望的参数,因此它会拒绝程序。简而言之,检查是在运行时完成的,然后程序可能会失败。     
        我不确定在用F#编写代码时这将有多大帮助,但是我认为这也可以在Haskell中轻松完成:
type \'a RecFunction  = RecFunction of (\'a -> \'a RecFunction)
let rec apply (f: \'a RecFunction) (lst: \'a list) = 
    match (lst,f) with
    | ([],_) -> f
    | ((x::xs), RecFunction z) -> apply (z x) xs
在这种情况下,所讨论的\“ f \”是使用可允许递归数据类型定义的已区分联合定义的。我猜这可以用来解决提到的问题。     
在其他一些人的帮助和输入下,我定义了一种实现此目标的方法(使用自定义列表类型),这与以前的答案有些不同。这是一个古老的问题,但是似乎仍然需要访问,因此我将添加完整的方法。 我们使用一个扩展名(GADT),其列表类型与Daniel Wagner的有点类似,但是具有标记功能类型而不是Peano编号。让我们逐段浏览代码。首先,我们设置扩展名并定义列表类型。数据类型是多态的,因此在此公式化中,参数不必具有相同的类型。
{-# LANGUAGE GADTs #-}

-- n represents function type, o represents output type
data LApp n o where
  -- no arguments applied (function and output type are the same)
  End :: LApp o o
  -- intentional similarity to ($)
  (:$) :: a -> LApp m o -> LApp (a -> m) o

infixr 5 :$ -- same as :
让我们定义一个可以接受这样的列表并将其应用于函数的函数。这里有些类型的骗术:函数的类型为
n
,仅当此类型与列表类型的
n
标记匹配时,才会编译对
listApply
的调用。通过不指定输出类型
o
,我们可以在其中留出一些自由(创建列表时,我们不必立即完全固定可应用的功能类型)。
-- the apply function
listApply :: n -> LApp n o -> o
listApply fun End = fun
listApply fun (p :$ l) = listApply (fun p) l
而已!现在,我们可以将函数应用于存储在列表类型中的参数。期望更多? :)
-- showing off the power of AppL
main = do print . listApply reverse $ \"yrruC .B lleksaH\" :$ End
          print . listApply (*) $ 1/2 :$ pi :$ End
          print . listApply ($) $ head :$ [1..] :$ End
          print $ listApply True End
不幸的是,我们被锁定在列表类型中,我们不能只转换普通列表以使用
listApply
。我怀疑这是类型检查器的根本问题(类型最终取决于列表的值),但是老实说,我不确定。
-- Can\'t do this :(
-- listApply (**) $ foldr (:$) End [2, 32]
如果您对使用异构列表感到不舒服,您所要做的就是向
LApp
类型添加一个额外的参数,例如:
-- Alternative definition
-- data FList n o a where
--   Nil :: FList o o a
--   Cons :: a -> FList f o a -> FList (a -> f) o a
此处的“ 5”表示参数类型,应用的函数还必须接受所有相同类型的参数。     
        这并不完全是您对原始问题的答案,但是我认为这可能是您的用例的答案。
pure f <*> [arg] <*> [arg2] ...
-- example
λ>pure (\\a b c -> (a*b)+c) <*> [2,4] <*> [3] <*> [1]
[7,13]
λ>pure (+) <*> [1] <*> [2]
[3]
list的适用实例比这个超级狭窄的用例要广泛得多...
λ>pure (+1) <*> [1..10]
[2,3,4,5,6,7,8,9,10,11]
-- Or, apply (+1) to items 1 through 10 and collect the results in a list

λ>pure (+) <*> [1..5] <*> [1..5]
[2,3,4,5,6,3,4,5,6,7,4,5,6,7,8,5,6,7,8,9,6,7,8,9,10]

{- The applicative instance of list gives you every possible combination of 
elements from the lists provided, so that is every possible sum of pairs 
between one and five -}

λ>pure (\\a b c -> (a*b)+c) <*> [2,4] <*> [4,3] <*> [1]
[9,7,17,13]
{- that\'s -  2*4+1, 2*3+1, 4*4+1, 4*3+1
Or, I am repeating argC when I call this function twice, but a and b are 
different -}
λ>pure (\\a b c -> show (a*b) ++ c) <*> [1,2] <*> [3,4] <*> [\" look mah, other types\"]
[\"3 look mah, other types\",\"4 look mah, other types\",\"6 look mah, other types\",\"8 look mah, other types\"]
因此,确切地说,这不是一个相同的概念,但是它包含了许多组成用例,并增加了一些其他用例。     

要回复问题请先登录注册