Святилище Js и определение контравариантного функтора
Я пробую это с нуля, узнавая о Contravariants и более глубокое знание Sanctuary. Код "работает", но опять же у меня нет совершенно правильных типов.
Вот контравариант
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.
Если я отключу проверку типов, то она будет работать, как и ожидалось, поэтому логически она выглядит правильно сшитой. Я определил свою собственную версию контркарты
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))
Я неправильно понял это (тоже)
2 ответа
Вот шаблон для определения пользовательского типа и включения его в среду Sanctuary:
'use strict';
const {create, env} = require ('sanctuary');
const $ = require ('sanctuary-def');
const type = require ('sanctuary-type-identifiers');
// FooType :: Type -> Type
const FooType = $.UnaryType
('my-package/Foo')
('https://my-package.org/Foo')
(x => type (x) === 'my-package/Foo@1')
(foo => []);
// foo :: Foo
const foo = {
'constructor': {'@@type': 'my-package/Foo@1'},
'fantasy-land/contramap': function(f) {
throw new Error ('Not implemented');
},
};
const S = create ({
checkTypes: true,
env: env.concat ([FooType ($.Unknown)]),
});
S.I (foo);
// => foo
S.contramap (S.I) (foo);
// ! Error: Not implemented
Ты сочиняешь функции. Святилище определяет fantasy-land/map
а также fantasy-land/contramap
за Function a b
, поэтому нет необходимости в типе упаковки Contra.
> S.map (S.even) (s => s.length) ('Sanctuary')
false
> S.contramap (s => s.length) (S.even) ('Sanctuary')
false