gpt4 book ai didi

haskell - 不同类型对上的函数

转载 作者:行者123 更新时间:2023-12-03 15:01:36 24 4
gpt4 key购买 nike

有没有办法定义“pairmap”,如下所示:

pairmap f (x,y) = (f x, f y)

以便以下工作:
pairmap (+2) (1::Int, 2::Float)
pairmap succ (1::Int, 'a')
pairmap Just ('a', True)

等等

当然,在第一种情况下,两个元素都必须属于 Num 类。 ,在第二种情况下,两个类 Enum .然而,在第三种情况下,不需要限制。

回答(但可以改进)

以下代码 (ideone)解决了这个问题,但请注意,我的函数必须包装在一个数据类型中,该数据类型封装了输入和输出类型之间的关系以及对输入类型的任何约束。这可行,但有一些样板。如果我可以使用更少的样板来实现这一点,那就太好了,所以任何答案都会受到赞赏(尽管这个解决方案对于我的目的来说相当不错)。
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE RankNTypes #-}

import GHC.Exts (Constraint)

class Function f where
type Constraints f a :: Constraint
type instance Constraints f a = ()
type Result f a
type instance Result f a = a
applyFunc :: (Constraints f a) => f -> a -> Result f a

pairmap ::
(Function f, Constraints f a, Constraints f b) =>
f -> (a, b) -> (Result f a, Result f b)
pairmap f (x,y) = (applyFunc f x, applyFunc f y)

data NumFunc where
NumFunc :: (forall a. Num a => a -> a) -> NumFunc

instance Function NumFunc where
type Constraints NumFunc a = (Num a)
applyFunc (NumFunc f) = f

data EnumFunc where
EnumFunc :: (forall a. Enum a => a -> a) -> EnumFunc

instance Function EnumFunc where
type Constraints EnumFunc a = (Enum a)
applyFunc (EnumFunc f) = f

data MaybeFunc where
MaybeFunc :: (forall a. a -> Maybe a) -> MaybeFunc

instance Function MaybeFunc where
type Result MaybeFunc a = Maybe a
applyFunc (MaybeFunc f) = f

y1 = pairmap (NumFunc (+2)) (1::Int, 2::Float)
y2 = pairmap (EnumFunc succ) (1::Int, 'a')
y3 = pairmap (MaybeFunc Just) ('a', True)

main = do
print y1
print y2
print y3

回答 2

我觉得这样更好更灵活 (ideone) ,但同样,任何减少样板的改进都是受欢迎的:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableSuperClasses #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeApplications #-}

import GHC.Exts (Constraint)

data Func (c :: (* -> * -> Constraint)) where
Func :: (forall a b. c a b => a -> b) -> Func c

class (c a, a ~ b) => BasicConstraint c a b
instance (c a, a ~ b) => BasicConstraint c a b

numFunc = Func @(BasicConstraint Num)
enumFunc = Func @(BasicConstraint Enum)

class (c a, t a ~ b) => NewtypeConstraint c t a b
instance (c a, t a ~ b) => NewtypeConstraint c t a b

class EmptyConstraint a
instance EmptyConstraint a

maybeFunc = Func @(NewtypeConstraint EmptyConstraint Maybe)

applyFunc :: Func c -> (forall a b. c a b => a -> b)
applyFunc (Func f) = f

pairmap :: (c a a', c b b') => Func c -> (a, b) -> (a', b')
pairmap f (x,y) = (applyFunc f x, applyFunc f y)

main = do
print $ pairmap (numFunc (+2)) (1::Int, 2::Float)
print $ pairmap (enumFunc succ) (1::Int, 'a')
print $ pairmap (maybeFunc Just) ('a', True)

最佳答案

你的前两个例子比第三个更容易概括。

{-# LANGUAGE RankNTypes, ConstraintKinds, KindSignatures, AllowAmbiguousTypes, TypeApplications #-}

import GHC.Exts (Constraint)

pairmap :: forall (c :: * -> Constraint) d e. (c d, c e) =>
(forall a. (c a) => a -> a) -> (d,e) -> (d,e)
pairmap f (x,y) = (f x, f y)

此解决方案的警告是您需要显式实例化您正在使用的约束:
ghci> pairmap @Num (+1) (1 :: Int, 1.0 :: Float)
(2,2.0)

至于第三个,这里是一半的解决方案。如果第二种类型始终是第一种类型的参数化类型(如 f a ),那么您可以执行与上述相同的操作(尽管您的第一个示例停止工作 - 您可以通过将它们包装在 Identity 中使它们工作) .
pairmap' :: forall (c :: * -> Constraint) f d e. (c d, c e) =>
(forall a. (c a) => a -> f a) -> (d,e) -> (f d,f e)
pairmap' f (x,y) = (f x, f y)

再一次,在 GHCi
ghci> pairmap' @Num (Just . (+1)) (1 :: Int , 1.0 :: Float)
(Just 2,Just 2.0)

关于haskell - 不同类型对上的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42450219/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com