是否可以在Haskell中部分应用第n个参数?

是否可以在Haskell中部分应用第n个参数?,haskell,functional-programming,generic-programming,dependent-type,Haskell,Functional Programming,Generic Programming,Dependent Type,我很好奇是否可以编写一个函数apply\n,它接受一个函数、一个参数的数目和该参数的值,然后返回一个新的、部分应用的函数 我得到的感觉是,由于类型系统的原因,这是不可能的,但我无法给出令人满意的答案。我也找不到工作类型的签名 如果语言的类型比较松散,我想代码可能是这样的 apply_nth f 0 x = f x apply_nth f n x = \a -> apply_nth (f a) (n-1) x 有什么想法吗?你的感觉是正确的,这是不可能的。部分应用程序更改函数的类型,其方式

我很好奇是否可以编写一个函数
apply\n
,它接受一个函数、一个参数的数目和该参数的值,然后返回一个新的、部分应用的函数

我得到的感觉是,由于类型系统的原因,这是不可能的,但我无法给出令人满意的答案。我也找不到工作类型的签名

如果语言的类型比较松散,我想代码可能是这样的

apply_nth f 0 x = f x
apply_nth f n x = \a -> apply_nth (f a) (n-1) x

有什么想法吗?

你的感觉是正确的,这是不可能的。部分应用程序更改函数的类型,其方式取决于应用的参数。但是,如果该参数仅在运行时使用一个额外的参数编制索引,则编译器不知道该类型是什么,并且编译器必须对所有内容进行类型检查†。实际上,您需要得到一个结果,但Haskell不是一种依赖类型的语言

现在,实际上,如果你加入几个GHC扩展并引入几个奇怪的类型族,那么你实际上可以实现类似于这种依赖类型的东西。但老实说,我怀疑这是个好主意。你要这个干什么?如果你用8个以上的参数来处理函数,那么你可能做错了什么。对于更简单的函数,你可以定义8个组合符,每个组合符应用一个固定的参数位置

或者:一个类似的函数可能是合理的

apply_nth :: ([a] -> b) -> Int -> a -> [a] -> b
apply_nth f i a xs = f $ before ++ [a] ++ after
 where (before, after) = splitAt i xs
与参数列表不同,值列表很容易有数百个元素,因此在本例中,预先应用单个元素(在运行时编制索引)是有意义的



†这不仅仅是一项安全预防措施——这是必要的,因为类型在运行时甚至不存在,因此编译器需要完成准备所有可能依赖于类型的条件。这就是为什么Haskell像其他语言一样安全、简洁、快速、可扩展。

当然,有一点类型类的“魔力”:

applyNth
的常规类型表示,它接受索引(类型中编码的自然数)、参数、函数,然后返回函数

请注意这两个函数依赖关系。第一个表示给定索引、参数和输入函数,输出函数的类型是已知的。这是显而易见的。第二种说法是,给定索引和输入函数,
ApplyNth
能够查看函数内部并找出它需要的参数

此函数可以很好地进行类型推断:

>:t \x -> applyNth (SS SZ) x (^)
\x -> applyNth (SS SZ) x (^)
  :: (Num fn', Integral b) => b -> fn' -> fn'
>:t applyNth (SS SZ) 0 (^)
applyNth (SS SZ) 0 (^) :: Num fn' => fn' -> fn'
>:t applyNth (SS SZ) (0 :: Integer) (^)
applyNth (SS SZ) (0 :: Integer) (^) :: Num fn' => fn' -> fn'
>:t applyNth (SS SZ) ('a' :: Char) (^)

<interactive>:1:32: Warning:
    Could not deduce (Integral Char) arising from a use of `^'
    ...
applyNth (SS SZ) ('a' :: Char) (^) :: Num fn' => fn' -> fn'
>let squared = applyNth (SS SZ) 2 (^)
>:t squared
squared :: Num fn' => fn' -> fn'
>squared 3
9
>squared 100
10000
>let f a b c d e = mapM_ putStrLn 
      [ show n ++ ": " ++ x 
      | (n,x) <- zip [0..]  
          [show a, show b, show c, show d, show e] ]
>applyNth SZ 'q' $ 
 applyNth (SS $ SZ) [1,8,42] $ 
 applyNth SZ (True, 10) $ 
 applyNth (SS $ SS $ SS SZ) "abcd" $ 
 applyNth (SS $ SS $ SS SZ) pi $
 f
0: (True,10)
1: 'q'
2: [1,8,42]
3: 3.141592653589793
4: "abcd"

不是在任何称为“Haskell”的语言中,但是如果你看看格拉斯哥Haskell,包括不安全的函数,那么你可以部分地以你想要的方式应用。。。您必须正确指定参数的位置。这是一次可怕的黑客攻击。除非你对……非常满意,否则不要这样做。。。好。。不要这样做

这段代码是我问类似问题()时的代码

以及输出:

*Main> testBuild
"Look I have applied (\"argument 'b'\",42) and it seems to work."

请注意,如果我们错误地指定了参数索引(1),那么程序可能会出现segfault。

不是那种奇怪的类型族,但也不是超级好:

{-# LANGUAGE GADTs, DataKinds, TypeFamilies, TypeOperators #-}

import Data.Proxy

type family Fun as b where
    Fun '[]       b = b
    Fun (a ': as) b = a -> Fun as b

data SL as where
    Sn :: SL '[]
    Sc :: SL as -> SL (a ': as)

applyN :: Proxy c -> SL as -> Fun as (b -> c) -> b -> Fun as c
applyN p  Sn    f y = f y
applyN p (Sc s) f y = \x -> applyN p s (f x) y

main = print $ applyN Proxy (Sc (Sc Sn)) zipWith [1,2,3] (-) [6,5,4] -- [5,3,1]
我们还可以将
代理c
打包为
SL

data SL as c where
    Sn :: SL '[] c
    Sc :: SL as c -> SL (a ': as) c

applyN :: SL as c -> Fun as (b -> c) -> b -> Fun as c
applyN  Sn    f y = f y
applyN (Sc s) f y = \x -> applyN s (f x) y

main = print $ applyN (Sc (Sc Sn)) zipWith [1,2,3] (-) [6,5,4] -- [5,3,1]
或者,您可以简单地定义几个组合符:

z = id
s r f y x = r (f x) y
applyN = id

main = print $ applyN (s (s z)) zipWith [1,2,3] (-) [6,5,4] -- [5,3,1]

搜索“Haskell中的变量函数”,我相信我可能已经找到了。有些人可能仍在否认,但实际上,Haskell在相当长的一段时间内一直依赖于打字。这不是一个更大的项目或任何事情的一部分。我在考虑Lambda演算并将函数作为数据处理,我想知道这是否可以实现。谢谢你的回答!正如他所说:不要这样做!是的,当然值得重复。
{-# LANGUAGE GADTs, DataKinds, TypeFamilies, TypeOperators #-}

import Data.Proxy

type family Fun as b where
    Fun '[]       b = b
    Fun (a ': as) b = a -> Fun as b

data SL as where
    Sn :: SL '[]
    Sc :: SL as -> SL (a ': as)

applyN :: Proxy c -> SL as -> Fun as (b -> c) -> b -> Fun as c
applyN p  Sn    f y = f y
applyN p (Sc s) f y = \x -> applyN p s (f x) y

main = print $ applyN Proxy (Sc (Sc Sn)) zipWith [1,2,3] (-) [6,5,4] -- [5,3,1]
data SL as c where
    Sn :: SL '[] c
    Sc :: SL as c -> SL (a ': as) c

applyN :: SL as c -> Fun as (b -> c) -> b -> Fun as c
applyN  Sn    f y = f y
applyN (Sc s) f y = \x -> applyN s (f x) y

main = print $ applyN (Sc (Sc Sn)) zipWith [1,2,3] (-) [6,5,4] -- [5,3,1]
z = id
s r f y x = r (f x) y
applyN = id

main = print $ applyN (s (s z)) zipWith [1,2,3] (-) [6,5,4] -- [5,3,1]