当前位置: 首页 > 知识库问答 >
问题:

在Haskell中创建许多类似的NewTypes/TypeClass实例

支阳波
2023-03-14

我是Haskell的初学者,正在尝试学习类型类和类型。我有以下示例(它代表了我正在研究的代数中的一个实际问题),其中我定义了一个仅包装Num实例的类型,以及一个定义二进制操作baz的typeclass。

newtype Foo i = B i deriving (Eq, Ord, Show)

class Bar k where
    baz :: k -> k -> k

instance Num k => Bar (Foo k) where
    baz (B a) (B b) = B (f a b)

f :: Num a => a -> a -> a
f a b = a + b

当将bar定义为此实例时,我意识到我希望能够使用类型“改变”函数f。需要说明的是:我想提供一个函数f::numa=>a->a->a,并返回一个新的类型foo,它是bar的一个实例。假设我想这样做5次、10次,唯一的区别是不同的函数f。上面的代码我当然可以复制粘贴,但是我想知道有没有别的办法呢?

在我看来,我把事情弄糊涂了。在哈斯克尔做这样的事情最好的方法是什么?这是一个好的设计选择吗?我认为什么是对的/错的?为什么?

编辑:我意识到一个具体的示例可能有助于使问题更清楚(注意它可能看起来很复杂,我无法比这更简化代码。上面的问题包含了我认为相同的信息):我感兴趣的类型类是来自HaskellforMaths库的algebra k v:

class Algebra k b where
    unit :: k -> Vect k b
    mult :: Vect k (Tensor b b) -> Vect k b
newtype Basis i = T i deriving (Eq, Ord, Show)

type Expression k = Vect k (Basis Int)

instance Algebra k Basis where
    unit x = x *> return I
    mult = linear mult'
           where mult' (T x ,T y) = comm x y
           where comm a b = sum $ map (\c -> structure a b c *> t c) [0..n]

t :: Int -> Expression k
t a = return (T a)

共有1个答案

商骞仕
2023-03-14

你可以用反射。这是一个相当先进的技术,可能有更好的方法来解决你的问题,但你说的方式似乎这就是你要找的。

{-# LANGUAGE FlexibleContexts, RankNTypes, ScopedTypeVariables, UndecidableInstances #-}

import Data.Reflection
import Data.Proxy

class Bar k where
    baz :: k -> k -> k

newtype Foo f i = B i       -- f is a type level representation of your function
   deriving (Eq, Ord, Show)

instance (Num k, Reifies f (k -> k -> k)) => Bar (Foo f k) where
    baz (B a) (B b) = B (reflect (Proxy :: Proxy f) a b)

mkFoo :: forall i r. (i -> i -> i) -> i
      -> (forall f. Reifies f (i -> i -> i) => Foo f i -> r) -> r
mkFoo f x c = reify f (\(p :: Proxy f) -> c (B x :: Foo f i))

main = do
    mkFoo (+) 5 $ \foo1 -> do
    print $ foo1 `baz` B 5  -- 10

    mkFoo (*) 5 $ \foo2 -> do
    print $ foo2 `baz` B 5  -- 25

    print $ foo1 `baz` foo2 -- type error

这里有很多事情,所以请注意。

Reifies f (k -> k -> k)

是一个约束,它意味着fk->k->k类型函数的类型级表示形式。当我们reflect(Proxy::proxyf)(将类型f传递给reflect的一种很好的方式,因为直到最近才允许显式类型应用程序)时,我们会将函数本身收回。

mkFoo :: forall i r. (i -> i -> i) -> i
      -> (forall f. Reifies f (i -> i -> i) => Foo f i -> r) -> r
(forall f. Reifies f (i -> i -> i) => Foo f i -> r) -> r
exists f. ( Reifies f (i -> i -> i) , Foo f i )
mkFoo (+) 5 $ \foo -> -- what to do with foo

在函数中,foo的行为就像它具有类型foo f0 integer,其中f0是专门为该函数创建的全新类型。

它不允许我们从不同的fbaz一起foo,这是非常好的,但不幸的是,它不够聪明,不允许我们使用对mkfoo的不同调用,用同一个函数一起baz一起foo,因此:

mkFoo (+) 5 $ \foo1 -> mkFoo (+) 5 $ \foo2 -> foo1 `baz` foo2  -- type error
 类似资料:
  • 问题内容: 假设我们有一个抽象的@Entity Animal,以及几个扩展Animal的实体类,包括Dog,Cat,Monkey和Bat。 如何根据扩展实体的类过滤结果? 示例 :有复选框,用户可以在其中选择要检索的实体。 现在,我想使用在类中定义的(Named)Query检索实体。我可以在查询中放入哪种查询参数,以便仅返回Cat和Monkey对象? 问题答案: 我不确定JPA是否支持它,但是在H

  • 假设我们有一个带有签名的函数 是否有可能实施一个约束来确保a和b是不同的?那就是 这个问题的目的是了解更多关于Haskell的信息,并可能解决我面临的一个设计问题。如果a==b,我的特殊情况是没有意义的,所以我想在编译器级别禁止这样做。我可能会用一个完全不同的设计来解决这个问题,但这不仅仅是现在的重点--潘多拉盒子已经打开,我想知道类型级别上的等式约束是否可能。

  • 为了澄清我的问题,让我用一种或多或少相同的方式重新措辞: 为什么Haskell中有超类/类继承的概念?导致这种设计选择的历史原因是什么?例如,为什么有一个没有类层次结构的基库,只有相互独立的类型库是如此糟糕? 此外,我在类继承中看到的一个可能不太好的事情是:我认为一个类实例会默默地选择一个对应的超类实例,这可能是为该类型实现的最自然的实例。让我们把单子看作函子的子类。也许可以有不止一种方法来定义某

  • 我试着做一个选择题,有一个单一的,正确的答案,格式类似于上图。我正在做最后一个项目,当按钮动作时,它会提示用户说多项选择题。然而,在JavaFXML中,我没有看到这样做的方法。我只在Android系统中遇到过如何做到这一点。(https://www.geeksforgeeks.org/alert-dialog-with-singleitemselection-in-android/) 非常感谢您的

  • 本文向大家介绍创建许多相同类型的JavaScript对象?,包括了创建许多相同类型的JavaScript对象?的使用技巧和注意事项,需要的朋友参考一下 创建单个对象比创建相同类型的多个对象要容易得多。为了克服这一障碍,javascript提供了对象构造函数。使用此函数,首先,我们必须创建对象的类型,然后,我们需要声明对象的属性 。 示例 在下面的示例中,最初,我们声明了包含“名称”,“属性”,“年

  • 在过去一周左右的时间里,我一直在为Scala开发一个类型化、索引化的数组特性。我希望将该特征作为类型类提供,并允许库用户以他们喜欢的方式实现它。下面是一个示例,使用列表的列表来实现2D数组类型类: 这一切看起来都很好。我遇到的困难是,我希望将索引类型约束为一个已批准类型的列表(用户可以更改)。由于程序不是所有已批准类型的原始所有者,所以我想用一个typeclass来表示这些已批准类型,并让已批准类