是否可以在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]