简体   繁体   English

Haskell是否支持typeclass的匿名实例?

[英]Does Haskell support anonymous instances of typeclass?

I have the following code in F# (it's from a book) 我在F#中有以下代码(来自一本书)

open System.Collections.Generic

type Table<'T, 'U> =
   abstract Item : 'T -> 'U with get
   abstract Discard : unit -> unit

let memoizeAndPermitDiscard f =
     let lookasideTable = new Dictionary<_, _>(HashIdentity.Structural)
     {new Table<'T, 'U> with
      member t.Item
         with get(n) =
             if lookasideTable.ContainsKey(n) then
                 lookasideTable.[n]
             else
                 let res = f n
                 lookasideTable.Add(n, res)
                 res
      member t.Discard() = 
           lookasideTable.Clear()}
let rec fibFast =
     memoizeAndPermitDiscard (fun n ->  
     printfn "computing fibFast %d" n
     if n <= 2 then 1 else fibFast.[n - 1] + fibFast.[n - 2])

As we can see the abstract type Table take it's implementation in the function memoizeAndPermitDiscard . 如我们所见,抽象类型Table在函数memoizeAndPermitDiscard采用它的实现。 Can Haskell do the same? Haskell可以这样做吗?

Apologies in advance: I'm not an F# expert, so I may be misreading the F# code. 预先致歉:我不是F#专家,所以我可能会误读F#代码。 But if I'm reading it right it's fairly straightforward to translate to Haskell: 但是,如果我没看错的话,将其翻译为Haskell相当简单:

data Table t u = Table { get :: t -> IO u, discard :: IO () }

memoize :: Hashable t => (t -> u) -> IO (Table t u)
memoize f = do
    tbl <- newHashTable
    return Table
        { get = \t -> do
            result <- lookupHashTable t tbl
            case result of
                Nothing -> let u = f t in writeHashTable t u tbl >> return u
                Just u -> return u
        , discard = clearHashTable tbl
        }

I'm assuming some suitable hash table implementation here that offers newHashTable , lookupHashTable , writeHashTable , and clearHashTable . 我假设这里提供了newHashTablelookupHashTablewriteHashTableclearHashTable合适的哈希表实现。 Implementing these (or suggesting a library that offers them) is sort of beside the point of the question, I think. 我认为,实现这些功能(或建议提供一个提供这些功能的库)有点与问题无关。

I'm not an F# expert either, but I believe what you're describing is where you create a anonymous single-use subclass, by declaring in at the point where you create an object how it implements the methods of a superclass or interface? 我也不是F#专家,但是我相信您所描述的是在创建对象的那一刻声明在哪里创建匿名的一次性子类,该类是如何实现超类或接口方法的? So it's really an anonymous class , not an anonymous instance (or rather, it's no more anonymous than any other object-oriented instance, which typically don't have names inherently, only variable names storing references to them). 因此,它实际上是一个匿名 ,而不是匿名实例(或者,它比其他任何面向对象的实例都更匿名,后者通常没有固有的名称,只有变量名称存储对它们的引用)。

It doesn't really make sense to do that with Haskell type classes/instances. 使用Haskell类型的类/实例确实没有任何意义。 The reason is that a Haskell instance represents something very different from an OO instance. 原因是Haskell实例表示的对象与OO实例完全不同。

The instances of OO classes are objects (even the instances of interfaces are objects). OO类的实例是对象(甚至接口的实例也是对象)。 All of a class' methods will always be invoked on an instance of that class. 所有类的方法将始终在该类的实例上调用。 So it makes sense to create an anonymous subclass of an existing class or interface at the time you create a new object. 因此,在创建新对象时,创建现有类或接口的匿名子类是有意义的。 You basically say how that object implements the required methods, as an alternative to declaring a whole named class of objects that implement the methods the same way, which you could instantiate in multiple places. 基本上,您说的是该对象如何实现所需的方法,作为声明以相同方式实现方法的对象的整个命名类的替代方法,您可以在多个地方实例化该对象。

The instances of Haskell classes are types (which is why the're called type classes). Haskell类的实例是类型 (这就是为什么将其称为类型类)的原因。 All of the methods of a class must involve the type somehow, but there is no guarantee that they take an input of the type. 类的所有方法都必须以某种方式涉及类型,但是不能保证它们采用该类型的输入 For example, consider the class 1 : 例如,考虑1类:

class Monoid' a
  where mempty' :: a
        mappend' :: a -> a -> a

It doesn't really make sense to say an object is an instance of Monoid' ; 说一个对象Monoid'的实例并没有什么意义。 if I were to create a new object and I wanted to anonymously instantiate Monoid' , how would I define mempty' ? 如果我要创建一个新对象并且想匿名实例化Monoid' ,我将如何定义mempty' mempty' isn't an operation I could invoke on my new object, it's an operation that receives no inputs at all (not even an implicit "this") and produces a value 2 . mempty'不是我可以新对象调用的操作,它是根本不接收任何输入(甚至不包含隐式“ this”)并产生2的操作

And then there's things like: 然后是这样的:

class Functor' f
  where fmap :: (a -> b) -> (f a -> f b)

Nothing ever takes an input of a type f that is an instance of Functor' ; 没有任何东西 f的输入,它是Functor'的实例; it doesn't even make sense to talk about something that might, since the instances of the class Functor' are type constructors that need a type parameter to result in a type, not types that actually contain values. 谈论可能的事情甚至都没有意义,因为类Functor'的实例是类型构造函数,需要一个类型参数来产生一个类型,而不是实际包含值的类型。 So again, it just makes no sense at the point that I'm creating a new object to say "and here's how this object implements Functor' "). 再说一遍,在我创建一个新对象时说“这是没有意义的”,这就是该对象实现Functor' )。

It could potentially make sense to declare a new anonymous type locally, and declare how it implements some type classes at the same time. 在本地声明一个新的匿名类型 ,并声明它如何同时实现某些类型类可能是有意义的。 But Haskell has no syntax for it, no. 但是Haskell没有语法,没有。

Fortunately, you also don't need to create anonymous classes/instances in order to have a one-off collection of functions that conforms to a known interface. 幸运的是,您也无需创建匿名类/实例即可拥有符合已知接口的一次性功能。 Functions are first-class values too, so you can just have type whose fields are functions. 函数也是一等值,因此您只能使用其字段为函数的类型 Then anywhere you like you can create a new value of that type by providing a definition for all of the function fields. 然后,您可以在任何地方通过为所有功能字段提供定义来创建该类型的新值。 For example: 例如:

data MyInterface = MyInterface
  { foo :: Int -> Bool
  , bar :: Int -> String
  }

example :: MyInterface -> Int -> (Bool, String)
example impl x
  = (foo impl x, bar impl x)

main = do
  let impl = MyInterface { foo = even, bar = show }
  print $ example impl 7

The above program prints (False,"7") . 上面的程序打印(False,"7")


1 I'm using Monoid' rather than Monoid (and similarly Functor' ) because I'm using simplifications of the real classes. 1我使用的是Monoid'而不是Monoid (以及类似的Functor' ),因为我使用的是实类的简化。 You can see the real definition with :info Monoid in ghci (or look up the documentation) if you're interested. 如果您有兴趣,可以使用ghci中的:info Monoid查看真实定义(或查找文档)。

2 Or alternatively, the class Monoid' mandates that there simply exists a value of each type that instantiates it, and mempty' just is a reference to it. 2或者,类Monoid'要求仅存在每个实例化它的类型的值,而mempty'只是对其的引用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM