6

hxtでの作業のために、次の関数を実装しました。

-- | Construction of a 8 argument arrow from a 8-ary function. Same
-- implementation as in @Control.Arrow.ArrowList.arr4@.
arr8 :: ArrowList a => (b1 -> b2 -> b3 -> b4 -> b5 -> b6 -> b7 -> b8 -> c)
                -> a (b1, (b2, (b3, (b4, (b5, (b6, (b7, b8))))))) c
arr8 f = arr ( \ ~(x1, ~(x2, ~(x3, ~(x4, ~(x5, ~(x6, ~(x7, x8)))))))
               -> f x1 x2 x3 x4 x5 x6 x7 x8 )

haddock のコメントで述べたように、上記の関数は 8 項関数arr8を取り、8 つの引数の矢印を返します。私は次のような関数を使用します: to(x1 &&& x2 &&& ... x8) >>> arr8 fは矢印です。x1x8

私の質問: 大きなタプルの定義を避ける方法はありますか? のよりエレガントな実装はありarr8ますか?

情報: 関数arr4と同じコード スキーマを使用しました( arr4のソース コードを参照) 。

4

2 に答える 2

8

これは機能しますが、非常に深く壊れやすい型クラスの魔法に依存しています。また、タプル構造をもう少し規則的にする必要があります。特に、それは を好む型レベルの連結リストであるべき(a, (b, (c, ())))です(a, (b, c))

{-# LANGUAGE TypeFamilies #-}

import Control.Arrow

-- We need to be able to refer to functions presented as tuples, generically.
-- This is not possible in any straightforward method, so we introduce a type
-- family which recursively computes the desired function type. In particular,
-- we can see that
--
--     Fun (a, (b, ())) r ~ a -> b -> r

type family   Fun h      r :: *
type instance Fun ()     r =  r
type instance Fun (a, h) r =  a -> Fun h r

-- Then, given our newfound function specification syntax we're now in
-- the proper form to give a recursive typeclass definition of what we're
-- after.

class Zup tup where 
  zup :: Fun tup r -> tup -> r

instance Zup () where 
  zup r () = r

-- Note that this recursive instance is simple enough to not require 
-- UndecidableInstances, but normally techniques like this do. That isn't
-- a terrible thing, but if UI is used it's up to the author of the typeclass
-- and its instances to ensure that typechecking terminates.

instance Zup b => Zup (a, b) where 
  zup f ~(a, b) = zup (f a) b

arrTup :: (Arrow a, Zup b) => Fun b c -> a b c
arrTup = arr . zup

そして今、私たちはできる

> zup (+) (1, (2, ()))
3

> :t arrTup (+)
arrTup (+)
  :: (Num a1, Arrow a, Zup b n, Fun n b c ~ (a1 -> a1 -> a1)) =>
     a b c

> arrTup (+) (1, (2, ()))
3

特定のバリアントを定義したい場合、それらはすべてarrTup.

arr8 
  :: Arrow arr 
  => (a -> b -> c -> d -> e -> f -> g -> h -> r)
  -> arr (a, (b, (c, (d, (e, (f, (g, (h, ())))))))) r
arr8 = arrTup

最後に注目する価値があるのは、レイジーを定義するとuncurry

uncurryL :: (a -> b -> c) -> (a, b) -> c
uncurryL f ~(a, b) = f a b

Zup次に、ここで何が起こっているかを説明する方法での再帰ブランチを書くことができます

instance Zup b => Zup (a, b) where 
  zup f = uncurryL (zup . f)
于 2014-03-24T19:41:39.030 に答える