Получение полей записи на Haskell в зависимости от их типа - PullRequest
0 голосов
/ 08 февраля 2019

Я хотел бы определить класс типов для доступа к определенным полям в записях на основе их типа.В этом игрушечном примере у нас есть Failable (это просто Either), который может присутствовать в разных записях и включать разные типы.Мне интересно, можно ли было бы определить одну функцию failableFrom и позволить компилятору выбрать правильный экземпляр на основе контекста.

type Money  = Double
type Name   = String
type ErrMsg = String

class HasFailable a b where
  failableFrom :: a -> Either ErrMsg b

data SomeRecord = SomeRecord (Either ErrMsg Name) (Either ErrMsg Money)

instance HasFailable SomeRecord Name where
  failableFrom (SomeRecord name _) = name

instance HasFailable SomeRecord Money where
  failableFrom (SomeRecord _ money) = money

data SomeOtherRecord = SomeOtherRecord (Either ErrMsg Name)

instance HasFailable SomeOtherRecord Name where
  failableFrom (SomeOtherRecord name) = name

data SomeOtherOtherRecord = SomeOtherOtherRecord (Either ErrMsg Money)

instance HasFailable SomeOtherOtherRecord Money where
  failableFrom (SomeOtherOtherRecord money) = money



-- some record
record = SomeRecord (Right "John") (Right 200.0)

-- let the compiler decide what failableFrom function to use
moreMoney = fmap (\money -> money + 200.0) $ failableFrom record

Я спрашиваю это в основном излюбопытства о том, что возможно в Haskell.

1 Ответ

0 голосов
/ 08 февраля 2019

класс типов для доступа к определенным полям в записях на основе их типа

Нечто подобное может быть достигнуто с помощью общего программирования , техники для проверки структурытипы данных и определяющие функции, которые работают с различными типами данных в соответствии со структурой каждого.

Чтобы использовать общее программирование, необходимо включить расширение DeriveGeneric и импортировать GHC.Generics module.

{-# LANGUAGE DeriveGeneric #-}

import GHC.Generics

type Money  = Double
type Name   = String
type ErrMsg = String

data SomeRecord = SomeRecord (Either ErrMsg Name) (Either ErrMsg Money) deriving (Generic)

Написание функции «типизированный метод доступа» на основе генериков было бы сложно.К счастью, эта функциональность уже реализована в модуле Data.Generics.Product.Typed пакета generic-lens .Этот модуль предоставляет объектив typed, который позволяет нам нацеливать поля в записи по их (уникальному) типу.

(A lens - это значение, которое упаковывается вместегеттер и сеттер для поля записи. Пакет lens содержит основные определения и функции для работы с ними, в частности, функцию view для получения значения поля.)

import Control.Lens (view)
import Data.Generics.Product.Typed (typed)

moreMoney :: Either ErrMsg Money
moreMoney = fmap (\money -> money + 200.0) $ view typed record

Здесь компилятор определил, что нам нужно поле Money из-за сигнатуры типа.Но мы могли бы также использовать явное приложение типа :

{-# LANGUAGE TypeApplications #-}

moreMoney' = fmap (\money -> money + 200.0) $ view (typed @(Either _ Money)) record
...