И foo
, и bar
ограничивают то, что тип варианта должен содержать какой-либо тег, но они не препятствуют включению других тегов в вариант.Конкретные типы при применении fooToString bar
будут следующими:
bar :: Variant (bar :: Boolean, foo :: Int) -- v = (foo :: Int)
fooToString :: Variant (foo :: Int, bar :: Boolean) -> String -- v = (bar :: Boolean)
Как вы можете видеть, они идеально совместимы.
Внутренняя версия типов будет
bar :: forall r1 r2. RowCons "bar" Boolean r1 r2 => Variant r2
fooToString :: forall r1 r2. RowCons "foo" Int r1 r2 => Variant r2 -> String
, который будет специализироваться на
bar :: RowCons "bar" Boolean (foo :: Int) (foo :: Int, bar :: Boolean) => Variant (foo :: Int, bar :: Boolean)
fooToString :: RowCons "foo" Int (bar :: Boolean) (foo :: Int, bar :: Boolean) => Variant (foo :: Int, bar :: Boolean) -> String
Где, для bar
, r1
устанавливается на / специализируется на (foo :: Int)
и r2
становится (foo :: Int, bar :: Boolean)
.Аналогично для fooToString
.
Как видите, r1 не используется вне ограничения как для bar, так и для fooToString, что позволяет свободно выбирать их для удовлетворения ограничений.Было бы одинаково допустимо, чтобы компилятор добавил еще больше (неиспользованных) вариантов к типу, но это не изменило бы результат.
Редактировать:
Что-то, что являетсянеобычным в Pruescript является то, что не только функции, но и значения могут быть полиморфными.В этом случае bar
- это полиморфное значение, а fooToString
- это полиморфная функция.Это означает, что пользователи как bar
, так и fooToString
могут свободно выбирать любой тип для v
(или r1
/ r2
), поскольку тип содержит требуемые варианты.