从Haskell中的元组中提取第n个元素(其中n和元组被赋予参数)

时间:2018-03-02 20:44:01

标签: haskell tuples

有没有办法在Haskell中编写以下函数:

get_nth_element :: Int->(???)->(???)
get_nth_element n tpl = ...

我对元组的解决方案感兴趣,而不是列表,解决方案很简单。当然,元组可以聚合不同类型的原因。

中间目标是能够编写一个给定元组和值的函数,它返回一个将给定值添加到给定元组的元组。最终目标是编写一个通用的笛卡尔积函数,给定n个(可能不同类型的)列表的元组,返回所有得到的n维元组的笛卡尔积列表(使用,例如,应用运算符{{1} })。

这可以用C ++实现(使用可变参数模板和SFINAE),所以我假设应该有一种方法可以在Haskell中实现。

[更新] C ++(11及以上)有<$>, <*>std::get<i>(tuple)。例如,这会按预期编译和运行:

std::tuple_cat

本质上,这里需要一种机制来处理可变参数类型列表。我几乎不相信哈斯克尔没有那个。

[更新]注意:这不是关于从先验已知长度的元组访问先验已知元素。它是关于访问由变量元组中的变量索引给出的元素(长度和类型未知的先验)。前者意味着lambda存取器#include <iostream> #include <tuple> template<typename T, typename ...Args> std::tuple<T,Args...> grow_tuple(T v, std::tuple<Args...> t) { return std::tuple_cat(std::make_tuple(v), t); } int main(void) { std::tuple<int, std::string> t1{1, "Howdy!"}; int i = 13; auto t2 = grow_tuple(i, t1); std::cout<<"("<<std::get<0>(t2) <<","<<std::get<1>(t2) <<","<<std::get<2>(t2)<<")\n"; std::cout<<"Done!"<<std::endl; } 的固定定义,其中元组和元组长度中的感兴趣位置都是先验已知的。虽然我的问题没有做出任何先验已知的假设。我正在寻找的函数签名不采用这样的lambda访问器,而是采用元组内部位置索引的整数参数和通用元组参数(未知长度和包含类型的通用含义)。因此,这个问题与Haskell - Accessing a Specific Element in a Tuple不同。

2 个答案:

答案 0 :(得分:2)

经过验证的HList似乎与您的用例相符。

{-# LANGUAGE DataKinds, ExplicitForAll, GADTs, PolyKinds,
             TypeFamilies, TypeOperators, UndecidableInstances
#-}
import GHC.TypeLits(TypeError(..), ErrorMessage(..))

-- HList takes a list of types and creates a type that contains one value of each
data HList (ts :: [*]) where
  (:^:) :: t -> !(HList ts) -> HList (t:ts)
  -- strictness removes extra bottoms compared to tuples
  HNil  :: HList '[]
infixr 5 :^:

-- natural numbers are either zero or the successor of another
data Nat = Zero | Succ Nat
-- singletons
data SNat (n :: Nat) where
  SZero :: SNat Zero
  SSucc :: SNat n -> SNat (Succ n)

-- index into a normal list
type family IndexL (i :: Nat) (xs :: [k]) :: k where
  IndexL Zero (x:xs) = x
  IndexL (Succ n) (x:xs) = IndexL n xs
  IndexL i xs = TypeError (Text "Cannot index "
                      :<>: ShowType i
                      :<>: Text " into the list "
                      :<>: ShowType xs
                          )

-- index into an HList
index :: forall (i :: Nat) xs. SNat i -> HList xs -> IndexL i xs
index SZero (x :^: _) = x
index (SSucc n) (_ :^: xs) = index n xs
-- despite appearances, this function is total
-- you'll get a compiler error if you try to add an HNil case.

有许多实例(例如ReadEq)要编写/派生以使其有用。

main = do let test = 1 :^: "abc" :^: (7 :^: Zero :^: HNil) :^: HNil
          print $ index SZero test + 2
          print $ ord <$> index (SSucc$SZero) test
          print $ index SZero $ index (SSucc$SSucc$SZero) test

插入元素涉及在类型级别上添加type family Inject (x :: k) (i :: Nat) (xs :: [k]) :: [k] where ...并在值上添加相应的函数。编写类型Applicative f => HList [f a, f b, ...] -> f (HList [a, b, ...])的函数更难(在更多的打字意义上,而不是概念上),但是可行。

你没有得到很好的数字文字,但那真的只是&#34; meh&#34;。你可以使用TH来纠正它。

答案 1 :(得分:1)

通常不可能实现,因为元组中的每个元素都有不同的类型

以3个项目为例。在编译时无法确定输出类型

 nth :: (a,b,c) => Int -> (a,b,c) -> ???