【问题标题】:Sanctuary Js and Defining a Contravariant FunctorSanctuary Js 和定义逆变函子
【发布时间】:2018-06-14 01:08:00
【问题描述】:

我正在尝试从头开始学习逆变器和更深入的 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.

如果我禁用类型检查,那么它会按预期工作,所以从逻辑上讲,它似乎被正确地缝合在一起。我定义了自己的 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

因此,尽管讨论了逆变函子是否是解决这个问题的最佳方法(学习练习),问题是,在定义我自己的逆变函数实现时,我可以如何将 sanctuary 的 contramap 函数与启用类型检查


更新后添加代码:

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 并且它是 contramapg 它返回一个新的逆变函子包装 x = g(f(x)) 我是否也误解了这个

【问题讨论】:

  • @@type 必须存在于类型代表而不是值本身上。换句话说,x.constructor['@@type'] 而不是x['@@type']。你能提供Contra#fantasy-land/contramap的类型签名吗?我不清楚它与Function#fantasy-land/contramap 有何不同。
  • @davidchambers 感谢 cmets。我已将签名添加到 Contra 构造函数,它改变了错误。我会在上面更新。

标签: javascript functional-programming fantasyland sanctuary


【解决方案1】:

这是定义自定义类型并将其包含在 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

【讨论】:

    【解决方案2】:

    您正在编写函数。 Sanctuary 为Function a b 定义了fantasy-land/mapfantasy-land/contramap,因此不需要Contra 包装类型。

    > S.map (S.even) (s => s.length) ('Sanctuary')
    false
    
    > S.contramap (s => s.length) (S.even) ('Sanctuary')
    false
    

    【讨论】:

    • 谢谢 - 但是我读错了那个例子并没有表明实现是幻想土地兼容(github.com/fantasyland/fantasy-land/tree/v3.5.0#contravariant):contramap :: Contravariant f => f a ~> (b -> a) -> f b。那不是说 contramap 是逆变对象上的方法吗?
    • sanctuary-type-classes 提供了内置类型的实现,因此可以假装在使用 Sanctuary 时定义了 Function#fantasy-land/contramap
    • 意味着只要我们使用在避难所中定义的类型,它将符合幻想世界。但是(我是否正确)我正在尝试做的事情(作为学习练习,构建我自己的逆变类型并使用 sanctuary 的 contramap)不是设计 sanctuary 的工作方式(也不应该)。
    • Sanctuary 分派到自定义类型的fantasy-land/contramap 方法。不过,我的问题是,除了Function a bArrow a b 之外,我不知道可以提供这种方法的类型(例如,我可以命名六种可以提供fantasy-land/map 的类型)。
    • 我不认为我很欣赏这种区别。让我剖析一下我认为我理解的内容。 ArrayMaybe 等类型都提供map 而不是contramap。没有实现contramap 的“知名”类型。我认为这就是你所说的,我会同意(至少在我目前的理解水平上)。问题仍然存在,如果 sanctuary 将发送到 contramap 以获取自定义类型 - 我如何让 Sanctuary 相信我的类型有能力?非常感谢您对此(以及图书馆等)的帮助。
    猜你喜欢
    • 2016-10-28
    • 1970-01-01
    • 2019-10-01
    • 2010-11-07
    • 2021-01-22
    • 1970-01-01
    • 1970-01-01
    • 2017-10-04
    • 1970-01-01
    相关资源
    最近更新 更多