Существует (если пренебречь неупакованными типами) только один вид, типы которого на самом деле имеют какие-либо значения: *
. Все остальные типы не содержат типов как таковых, а только «сущности уровня типа». Компилятор может использовать их, чтобы определить что делать с фактическими типами и их значениями, но никогда невозможно иметь во время выполнения значение типа с чем-то вроде * -> Constraint
.
То, что *
тип для типов-значений, - это просто правило игры. Это хорошо по той же причине, по которой неплохо иметь сильную систему статических типов, которая предотвращает бессмысленные преобразования во время выполнения. Wat ?? < / sup> Или, давайте понимать это буквально, по той же причине, по которой вы не можете просто перепрыгивать своим королем через пешки, независимо от того, насколько привлекательно эта функция может выглядеть в конкретной ситуации, с которой вы столкнулись.
Если бы какое-то расширение действительно позволяло создавать значения из не *
-родственных типов, в частности * -> Constraint
, нам понадобилась бы целая куча неочевидных определений, чтобы прояснить, как на самом деле предполагается использовать эти «значения класса». Скорее всего, это будет тип записи, содержащий методы класса в качестве словаря. Но как именно ... спецификация могла бы стать настоящим кошмаром. И усложнение самого языка таким образом никоим образом не стоит затрат, поскольку 1. стандартный способ использования классов типов подходит как минимум для 95% всех приложений, а 2. когда вам действительно нужны классы овеществленного типа, вы можете легко сделать это с помощью GADT, ConstraintKinds или даже старых простых записей словаря, определенных вручную. Ничто из этого не требует искажения фундаментальных представлений о том, как язык обрабатывает значения, в отличие от не-*
-типов.
В любом случае ... давайте на минутку рассмотрим, как это может работать. Одно можно сказать наверняка: это не позволит вам писать что-нибудь столь же простое, как f str = 4
!
Учитывать
f1 :: forall a, b . Eq a -> Num b
Оба Eq a, Num b :: Constraint
, поэтому у нас будут значения типа Constraint
. По сути, это будет особый словарь методов для данного экземпляра. Таким образом, реализация f1
должна выглядеть примерно так:
f1 (EqDict (d_eq :: a -> a -> Bool))
= NumDict { dict_fromInteger = ??? :: Integer -> b
, dict_plus = ??? :: b -> b -> b
, ...
, dict_signum = ??? :: b -> b
}
Очевидно, что нет значимого способа определить все эти методы в результате. Все, что вы могли делать с таким «классом-функцией», - это «проецироваться» от более сильного класса к более слабому. Например. вы могли бы определить
monadApp :: forall m . Monad m -> Applicative m
monadApp (MonadDict {dict_return = d_ret, dict_bind = d_bd})
= ApplicativeDict { dict_pure = d_ret
, dict_app = \fs vs -> d_bd fs (\f -> d_bd vs $ d_ret . f) }
Этот конкретный вариант на самом деле был бы несколько полезен, но только потому, что Monad
(все еще, но не надолго!) отсутствует Applicative
в качестве суперкласса, который он просто должен иметь. Обычно не должно быть особых причин для явного «понижения» каких-либо классов, потому что отношения суперкласса (или кортеж-ConstraintKinds) делают это автоматически.
person
leftaroundabout
schedule
23.12.2014
Eq
? - person luqui   schedule 23.12.2014Functor
? Это(* -> *) -> Constraint
. Как это записать?(Functor f) something
или(Functor f) something? What if
something` нужно удерживать другое ограничение? Более того, что, если я захочу использовать один и тот же тип в разных местах, например([Ord a] -> [Ord a])
. Последнее кажется довольно многословным. И напоследок: а что насчетNum a, Ord a, Show a
? Как использовать ваш синтаксис в этой ситуации? - person Zeta   schedule 23.12.2014