Я пробую это с нуля, узнавая о Контравариантах и более глубоких знаниях Святилища.Код «работает», но опять же у меня нет точно правильных типов.
Вот Контравариант
const {contramap: contramapFl, extract } = require('fantasy-land');
const getInstance = (self, constructor) =>
(self instanceof constructor) ?
self :
Object.create(constructor.prototype) ;
// Contra a ~> g: a -> ?
const Contra = function(g){
const self = getInstance(this, Contra)
// :: F a ~> b -> a -> F b [ b -> a -> ? ]
self[contramapFl] = f => Contra( x => g(f(x)) )
self[extract] = g
self['@@type'] = 'fs-javascript/contra'
return Object.freeze(self)
}
// UPDATE adding type to constructor
Contra['@@type'] = 'fs-javascript/contra'
И моя попытка получить правильные типы
const $ = require('sanctuary-def');
const type = require('sanctuary-type-identifiers');
const Z = require('sanctuary-type-classes') ;
const isContra = x => type (x) === 'fs-javascript/contra'
const ContraType = $.UnaryType(
'fs-javascript/contra',
'http://example.com/fs-javascript#Contra',
isContra,
x => [x[extract]])($.Unknown)
Тогда мой тест
const {create, env} = require('sanctuary');
const {contramap} = create({checkTypes: true, env: env.concat(ContraType) });
const isEven = Contra(x => x % 2 === 0) ;
console.log(Z.Contravariant.test(isEven)) // => true
const isLengthEvenContra = contramap(y => y.length, isEven)
const isStringLengthEven = isLengthEvenContra[extract]
console.log(isStringLengthEven("asw")) //=> ERROR
TypeError: Type-variable constraint violation
contramap :: Contravariant f => (b -> a) -> f a -> f b
^
1
1) "fs-javascript/contra" :: String
f => Contra( x => g(f(x)) ) :: Function, (c -> d)
x => x % 2 === 0 :: Function, (c -> d)
Since there is no type of which all the above values are members, the type-variable constraint has been violated.
Если я отключу проверку типов, то она будет работать, как и ожидалось, поэтому логически она выглядит правильно сшитой.Я определил свою собственную версию contramap
const def = $.create({ checkTypes: true, env: $.env.concat(ContraType) });
const contramap2 =
def('contramap2', {}, [$.Unknown, ContraType, ContraType],
(f, x) => {
const z = x[contramapFl](f)
return z
}
)
. Затем я перезапустил тест:
const isEven = Contra(x => x % 2 === 0) ;
console.log(Z.Contravariant.test(isEven)) // => true
const isLengthEvenContra = contramap2(y => y.length, isEven)
const isStringLengthEven = isLengthEvenContra[extract]
console.log(isStringLengthEven("asw")) //=> false
. Таким образом, я выдержал дискуссию о том, является ли контравариантный функтор лучшим подходом к этой проблеме (обучениеупражнение), вопрос в том, как при определении собственной реализации контраварианта я могу использовать функцию контракарты святилища с включенной проверкой типов .
после обновления, добавив код:
Contra['@@type'] = 'fs-javascript/contra'
изменил ошибку на:
TypeError: Type-variable constraint violation
contramap :: Contravariant f => (b -> a) -> f a -> f b
^ ^
1 2
1) 3 :: Number, FiniteNumber, NonZeroFiniteNumber, Integer, NonNegativeInteger, ValidNumber
2) x => x % 2 === 0 :: Function, (c -> d)
Since there is no type of which all the above values are members, the type-variable constraint has been violated.
// Contra (Integer -> Boolean)
const isEven = Contra(x => x % 2 === 0) ;
// String -> Integer
const strLength = y => y.length
// I Think: Contra (String -> (Integer -> Boolean))
const isLengthEvenContra = contramap(strLength, isEven)
// (String -> (Integer -> Boolean))
const isStringLengthEven = isLengthEvenContra[extract]
Мое пониманиеиз контравариантного функтора заключалось в том, что он предварительно составлял функцию внутри него, причем функция передавалась через contramap
.Таким образом, если контравариант содержит функцию f
и равен contramap
с g
, он возвращает новое контравариантное обертывание функтора x = g(f(x))
Не понял ли я это (тоже)