2016-10-27 8 views
2

Пусть следующие типы данных определяются:Функции с более высокими видами?

data X a = X {getX :: a} 
data Y a = Y {getY :: a} 
data Z a = Z {getZ :: a} 

Должен ли быть три отдельных функций, getX, getY и getZ? Мне кажется, что там может быть функция, определенная что-то вроде этого:

get :: forall (τ :: (* -> *)) (a :: *). τ a -> a 
get (_ x) = x 

Очевидно, что это не является допустимым стандартом Haskell, но есть очень много расширений к GHC, которые, кажется, что они могли бы иметь решение (RankNTypes, ExistentialQuantification , DataKinds и т. Д.). Помимо простой причины избежать крошечного набора текста, есть преимущество избежать загрязнения пространства имен, создаваемого решением записи. Я предполагаю, что это на самом деле просто более неявное решение, чем класс типа, как это:

class Get f where 
    get :: f a -> a 

Однако представляется, что определение обобщенной функции было бы более полезным, чем класс типа, так как тот факт, что она неявно что он может использоваться во многих других местах, так же, как используется ($) или (.). Поэтому мой вопрос состоит из трех частей: есть ли способ сделать это, это хорошая идея, а если нет, то что лучше?

+0

Что бы ваша функция 'get' выполняла, если ему был предоставлен список (т. Е. Если' τ' был '[]')? – duplode

+3

Ну, это делает неявное предположение, что это не сработает.Нет причин предполагать тип с конечной переменной типа вида * имеет один конструктор, который принимает один аргумент рассматриваемого типа. – Carl

ответ

10

Как насчет этого типа?

newtype Pred a = Pred (a -> Bool) 

Или этот?

data Proxy a = Proxy 

Там нет никакого способа, чтобы получить a из Pred a. Вы можете поставить только a. Также не существует способа получить a из Proxy a, потому что внутри него нет a.

Таким образом, функция get :: forall f a. f a -> a не может существовать вообще. Вам нужно использовать класс типа, чтобы различать те типы f, из которых вы можете извлечь a и те, из которых вы не можете.

3

Ну, этот неограниченный общий тип get, безусловно, не может работать. Это также позволит вам извлечь, скажем, значение Void от Const() :: Const() Void.

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

{-# LANGUAGE TypeFamilies, DeriveGeneric, DeriveAnyClass #-} 
import GHC.Generics 

class Get τ where 
    get :: τ a -> a 

data X a = X a deriving (Generic1, Get) 
data Y a = Y a deriving (Generic1, Get) 
data Z a = Z a deriving (Generic1, Get) 

Чтобы действительно получить эту работу, нам нужны только два странных экземпляры представления типа:

instance Get f => Get (M1 i t f) where get = get . unM1 
instance Get Par1 where get = unPar1 

Сейчас фактическая реализация для X, Y и Z могут просто использовать подпись по умолчанию и уменьшить извлечение до базового типа-представления. С этой целью определите класс таким образом:

{-# LANGUAGE DefaultSignatures #-} 

class Get τ where 
    get :: τ a -> a 
    default get :: (Generic1 τ, Get (Rep1 τ)) => τ a -> a 
    get = get . from1 

 Смежные вопросы

  • Нет связанных вопросов^_^