有没有办法在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不同。
答案 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.
有许多实例(例如Read
,Eq
)要编写/派生以使其有用。
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) -> ???