Как получить «непредсказуемую» перегрузку для возвращаемого типа, работающего в Haskell? - PullRequest
0 голосов
/ 18 декабря 2018

У меня есть несколько типов экземпляров.Давайте назовем их A, B и C. Все они являются экземплярами класса типов X. Теперь я хотел бы создать отдельную функцию create, которая создает экземпляр A, B или C с учетом некоторого ввода (скажем, строки).Система типов не может знать, какой ввод будет давать какой тип.Это то, что Хаскеллу не нравится, и я думаю, что знаю ответ, но хочу быть уверенным.Текущая ошибка, которую я получаю:

• Couldn't match expected type ‘c’ with actual type ‘GCCCommand’
  ‘c’ is a rigid type variable bound by
    the type signature for:
      compiler :: forall c. CompilerCommand c => String -> c
    at src/System/Command/Typed/CC.hs:29:1-44
• In the expression: gcc path
  In an equation for ‘compiler’:
      compiler path
        | exe == "g++" || exe == "gcc" || exe == "cc" || exe == "cpp"
        = gcc path
        where
            exe = takeFileName path
• Relevant bindings include
    compiler :: String -> c
      (bound at src/System/Command/Typed/CC.hs:31:1)

Означает ли это, как я подозреваю, что в данном конкретном случае невозможно перегрузить тип возвращаемого значения, потому что компилятор не может заранее знать, какданные будут выглядеть в памяти?Как бы вы пошли, чтобы реализовать эту функцию?Я думал о создании чего-то вроде следующего:

data SuperX = SuperA A | SuperB B | SuperC C

create :: String -> SuperX
-- create can now be implemented

instance X SuperX where
  -- a lot of boilerplate code ...

Однако стандартный код подсказывает, что это можно сделать лучше.Это действительно лучший способ сделать это?

1 Ответ

0 голосов
/ 19 декабря 2018

Зависит от того, что вам нужно с ним делать.

Если ваша последующая обработка не заботится , если она получает A, B или Cпросто он получает что-то, что реализует X ...

restOfProgram :: X a => a -> ThingIWantToCompute

Тогда вы можете использовать передачу продолжения:

parseABC :: (X a => a -> r) -> String -> Maybe r
parseABC f "A" = Just (f A)
parseABC f ('B':xs) = Just (f (B xs))
parseABC f ('C':xs) = Just (f (C (read xs)))
parseABC _ _ = Nothing

Или оболочку экзистенциальных данных:

data SomeX where
  SomeX :: X t => t -> SomeX

parseABC :: String -> Maybe SomeX
parseABC "A" = Just (SomeX A)
parseABC ('B':xs) = Just (SomeX (B xs))
parseABC ('C':xs) = Just (SomeX (C (read xs)))
parseABC _ _ = Nothing

restOfProgram' :: SomeX -> ThingIWantToCompute
restOfProgram' (SomeX t) = restOfProgram t

Если поздняя обработка имеет разные пути для A, B или C, вы, вероятно, захотите вернуть тип суммы, такой как SuperX.

...