How can I move this instance method definition to class default?

流过昼夜 提交于 2019-12-23 15:19:32

问题


Consider this class, and an example instance.

The purpose is to provide a type level switch that allows to convert the base type — Int, in this case, — to a proven predicate subtype, for future use. Granted, the class is somewhat contrived, but I extracted it from actual code, and I would populate it with more useful methods, save that I am stuck.

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeApplications #-}

module Collection where

import Data.Tagged

-- $setup
--
-- λ :set -XTypeApplications

class Collected phantom
  where
    type Element phantom = r | r -> phantom
    type Element phantom = Tagged phantom Int

    type Collection phantom = r | r -> phantom
    type Collection phantom = Tagged phantom [Int]

    collection :: Collection phantom

    inCollection :: Int -> Maybe (Element phantom)

data Primes

instance Collected Primes
  where
    type Element Primes = Tagged Primes Int
    type Collection Primes = Tagged Primes [Int]

    collection = Tagged [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

    -- inCollection :: Int -> Maybe (Element Primes)
    inCollection element
        | element `elem` unTagged (collection @Primes) = Just $ Tagged element
        | otherwise = Nothing

-- ^
-- λ inCollection @Primes 7
-- Just (Tagged 7)
-- λ inCollection @Primes 8
-- Nothing

(This is a runnable code complete with repl tests that pass as written.)

I will be dealing with several subtypes over the same base type that only differ by the definition of collection, while the method of proof is consistently lookup. So there is no reason not to have a default code for that method. However, I was not able to devise such code. My first draft fails to type check, while the second, adjusted draft runs, but does not seem to terminate.

Here is the first draft:

...
{-# LANGUAGE DefaultSignatures #-}
...

class Collected phantom
...
    inCollection :: Int -> Maybe (Element phantom)
    default inCollection :: ( Element phantom    ~ Tagged phantom  Int
                            , Collection phantom ~ Tagged phantom [Int] )
                         => Int -> Maybe (Element phantom)
    inCollection element
        | element `elem` unTagged collection = Just $ Tagged element
        | otherwise = Nothing
...

Here is the second:

...
{-# LANGUAGE ScopedTypeVariables #-}
...

class Collected phantom
...
    inCollection :: ...
    default inCollection :: ...
    inCollection ...
      where
        collection = (collection :: Collection phantom)

(Only the added parts are shown. The corresponding method definition in the instance was removed. In the second draft, the only addition aside from the pragma is the last line that attempts to type collection.)

 

What is the problem? What can be done?


回答1:


It appears that the following will work. The idea is to instantiate collection at the right type in the default (with collection @phantom) which also requires ScopedTypeVariables.

Update: I see now that this is what you tried to do with your second attempt. The problem is that your where clause defined an infinite loop, as the collection on the RHS is the same collection being bound on the LHS (insert facepalm as appropriate).

{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeApplications #-}

module Collection where

import Data.Tagged

class Collected phantom
  where
    type Element phantom = r | r -> phantom
    type Element phantom = Tagged phantom Int

    type Collection phantom = r | r -> phantom
    type Collection phantom = Tagged phantom [Int]

    collection :: Collection phantom

    inCollection :: Int -> Maybe (Element phantom)
    default inCollection :: ( Collection phantom ~ Tagged phantom [Int]
                            , Element phantom ~ Tagged phantom Int)
                            => Int -> Maybe (Element phantom)
    inCollection element
        | element `elem` unTagged (collection @phantom) = Just $ Tagged element
        | otherwise = Nothing

data Primes

instance Collected Primes
  where
    type Element Primes = Tagged Primes Int
    type Collection Primes = Tagged Primes [Int]

    collection = Tagged [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]


来源:https://stackoverflow.com/questions/51502879/how-can-i-move-this-instance-method-definition-to-class-default

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!