Я играю вокруг следующих уроков теории Bartosz Milewski на youtube. Он описывает функторы Const и Identity, поскольку "базовые" функторы могут быть получены из (вероятно, либерального перефразирования с моей стороны).
Моя проблема, внедрив ES6+/fantasy-land (не важная) версия функтора, появляется, когда я начинаю интегрироваться с библиотекой Sanctuary для карты и трубы.
Реализация довольно проста
const {map: flMap, extract } = require('fantasy-land');
const getInstance = (self, constructor) =>
(self instanceof constructor) ?
self :
Object.create(constructor.prototype) ;
const Identity = function(x){
const self = getInstance(this, Identity)
self[flMap] = f => Identity(f(x))
self[extract] = () => x
return Object.freeze(self)
}
Вот несколько простых применений (поскольку я также работал с линзами, получающими ион)
// USAGE
const {map, pipe, curry} = require("sanctuary")
const extractFrom = x => x[extract]()
const setter = (f, x) => (pipe([
Identity,
map(f),
extractFrom
])(x))
const double = x => x + x
console.log(Identity(35)) //=> 35
console.log(map(double, Identity(35))) // ERROR Should be Identity(70)
console.log(setter(double, 35)) // ERROR Should be: 70
TypeError: Type-variable constraint violation map :: Functor f => (a -> b) -> f a -> f b ^ ^ 1 2 1) 35 :: Number, FiniteNumber, NonZeroFiniteNumber, Integer, NonNegativeInteger, ValidNumber 2) () => x :: Function, (c -> d) f => Identity(f(x)) :: Function, (c -> d) Since there is no type of which all the above values are members, the type-variable constraint has been violated.
Однако функтор Const работает немного лучше (нет f, вызванного на карте)
const Const = function(x) {
const self = getInstance(this, Const)
self[map] = _ => Const(x)
self[extract] = () => x
return Object.freeze(self)
}
const getter = (f, x) => (pipe([
Const,
map(f),
extractFrom
])(x))
console.log(getter(double, 35)) //=> 35
Далее все "логически обосновано", как доказано, удалив проверку типа
const {create, env} = require('sanctuary');
const {map, pipe, curry} = create({checkTypes: false, env: env});
или заменить святилище рамдой. Так что это похоже на некоторую проблему согласованности типов с функцией отображения идентификационной информации.
Вопрос в том, как мне заставить все эти части играть вместе в стиле счастливого рода.
Вам нужно будет определить конструктор типа для вашего типа (IdentityType :: Type → Type
) и включить IdentityType ($.Unknown)
в среду Sanctuary, как описано в документации S.create
. В частности, вам понадобится что-то вроде этого:
// IdentityType :: Type -> Type
const IdentityType = $.UnaryType
('my-package/Identity')
('http://example.com/my-package#Identity')
(x => type (x) === Identity['@@type'])
(identity => [Z.extract (identity)]);
const S = create ({
checkTypes: process.env.NODE_ENV !== 'production',
env: env.concat ([IdentityType ($.Unknown)]),
});
В вышеприведенном фрагменте $
относится к sanctuary-def, Z
относится к классам type
святилища, а type
относится к идентификаторам type
святилища.
S.concat :: Semigroup a => a -> a -> a
. Учитывая S.concat (x) (y)
Sanctuary должен сначала определить все типы, членом которых является x
. Затем он должен отфильтровать этот набор, включив в него только типы, членом которых также является y
. Если результирующий набор является пустым набором, проверка типа не выполняется. Таким образом, SI (Identity (42))
не сможет проверить тип, если Identity (42)
не является членом какого-либо типа в среде.
extractFrom
, кажется, не определен. Это простоfoo => foo.extract()
?