2016-06-12 9 views
3

В Haskell вы можете применить fmap к двум функциям, которые в основном являются составной частью композиции. Вы можете даже составить fmap, чтобы включить функциональный состав функций с более высокой степенью (fmap . fmap).Как реализовать функтор, чтобы карта могла применяться к двум функциям?

Это работает, потому что функции являются функторами.

Как бы такой функтор (или соответствующий метод map) был реализован в Javascript?

Это то, что я пытался до сих пор:

funcProto = { 
    map(f) { return y => f(this.x(y)) } 
}; 

function func(x) { 
    return Object.assign(Object.create(funcProto), {x: x}); 
} 

const comp = f => g => x => f(g(x)); 
const map = f => ftor => ftor.map(f); 
const sub = y => x => x - y; 
const sqr = x => x * x; 
const inc = x => x + 1; 

Это работает для нормальной функции состава:

func(sqr).map(inc)(2); // 5 

Однако, это не работает для составленной версии map:

const map2 = comp(map)(map); 
map2(sub)(sub)(10)(5)(4); // Error 

Я думаю, что я слишком сильно адаптирую себя к традиционному способу работы им в Javascript. Функции как функторы ведут себя иначе, чем список или, может быть.

+0

«* функции традиционного способа реализованы в Javascript *» - их нет. – Bergi

ответ

2

В Haskell все функции. В вашем javascript некоторые из ваших функций представлены как func s с методом .x(), а некоторые из них являются родными Function s. Это не сработает.

Вот три подхода:

const sub = y => x => x - y; 
const sqr = x => x * x; 
const inc = x => x + 1; 
const comp = f => g => x => f(g(x)); 
  • простые функции, не методы.

    const fmap = comp; // for functions only 
    console.log(fmap(inc)(sqr)(1)) // 5 
    const fmap2 = comp(fmap)(fmap); 
    console.log(fmap2(sub)(sub)(10)(5)(4)); // 9 
    
  • расширения родной Function с, используя fmap как метод:

    Function.prototype.fmap = function(f) { return comp(this)(f); }; 
    console.log(sqr.fmap(inc)(1)); // 5 
    const fmap2 = comp.fmap(comp) // not exactly what you want, works just like above 
    Function.prototype.fmap2 = function(f) { return this.fmap(g => g.fmap(f)); } // better 
    console.log(sub.fmap2(sub)(10)(5)(4)); // 9 
    
  • строит свой собственный тип функции (также в ES6):

    function Func(f) { 
        if (!new.target) return new Func(f); 
        this.call = f; 
    } 
    // Ahem. 
    const sub = Func(y => Func(x => x - y)); 
    const sqr = Func(x => x * x); 
    const inc = Func(x => x + 1); 
    const comp = Func(f => Func(g => Func(x => f.call(g.call(x))))); 
    // Now let's start 
    const fmap = Func(f => Func(x => x.fmap(f))); // a typeclass! 
    Func.prototype.fmap = function(f) { return comp(this)(f); }; // an instance of the class! 
    console.log(fmap.call(inc).call(sqr).call(1)); // 5 
    const fmap2 = comp.call(fmap).call(fmap); 
    console.log(fmap2.call(sub).call(sub).call(10).call(5).call(4)); // 9 
    
+0

'const fmap = comp;' очень юмористический, вопрос ответил. Серьезно, 'fmap' и' (.) 'Имеют разные типы в Haskell, поэтому я задавался вопросом, почему они эквивалентны (если fmap применяется к двум функциям). И вы просто говорите 'fmap === comp', справляйтесь с ним. Итак, нет никакой практической разницы между 'fmap' и' comp' в этом контексте, и, вероятно, нет смысла строить версию 'comp' для методов в Javascript. Сначала я задал этот вопрос, чтобы получить более глубокое понимание «fmap» и функторов и тот факт, что функции являются функторами. – ftor

+1

Поскольку 'fmap' - это метод в классе типа« Functor », он определен для различных экземпляров« Functor »(например, для списков), из которых одни функции. Вот почему 'fmap' имеет другой (более общий) тип, чем' .'. Для функций 'fmap' действительно совпадает с' .'. https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#line-638 (Извините, я был в замешательстве в своем предыдущем комментарии, который уже удален.) – FPstudent

0

Простите меня если я ошибаюсь, но вы, кажется, путаетесь о Functor с в Хаскелле. Класс Functor типа в Haskell представляет типы данных, на которых «карте» (fmap быть более точным) определяется:

https://hackage.haskell.org/package/base-4.9.0.0/docs/Data-Functor.html

Например, списки ([] в Haskell) являются примером Functor, как:

fmap (\x->x+1) [1,2,3] -- returns [2,3,4] 

Как указано в приведенной выше документации, это верно, что fmap (f . g) должно быть эквивалентно fmap f . fmap g (напомним, что . в Haskell означает функцию композиции, то есть, (f.g) x равно f(g(x))).Например, пусть

f x = x + 1 

и:

g y = y * 2 

Тогда

fmap (f.g) [1,2,3] -- equivalent to [(f.g) 1, (f.g) 2, (f.g) 3] 

и

(fmap f . fmap g) [1,2,3] -- equivalent to (fmap f (fmap g [1,2,3])) 

эквивалентны и возвращают [3,5,7].

Array s в JavaScript уже Functor в этом смысле, так как у них есть map.

const f = x => x + 1; 
    const g = y => y * 2; 
    const comp = f => g => x => f(g(x)); 
    const fmap_array = f => a => a.map(f); 

    fmap_array (comp(f)(g)) ([1,2,3]); // [3,5,7] 
    (comp (fmap_array(f)) (fmap_array(g))) ([1,2,3]); // [3,5,7] 

Или, вы можете это сделать, если вы хотите:

Array.prototype.fmap = function(f) { return this.map(f); } 
    [1,2,3].fmap(f); // [2,3,4] 
    [1,2,3].fmap(g); // [2,4,6] 
    [1,2,3].fmap(comp(f)(g)); // [3,5,7] 
    [1,2,3].fmap(g).fmap(f); // [3,5,7] 

P.S.

Теперь я понимаю, что вы имеете в виду в вашем вопросе - функции (-> в Haskell) также является экземпляром Functor, действительно определяется как функция состава fmap f g = (f . g):

https://hackage.haskell.org/package/base-4.9.0.0/docs/Data-Functor.html#control.i:ic:Functor:Functor:28

https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#line-638

для чего-то похожие на: JavaScript,

const fmap_func = f => g => comp(f)(g); // or "const fmap_func = comp;"! 
    fmap_func(f)(g)(42); // 85 

или, если хотите:

Function.prototype.fmap = function(f) { return comp(f)(this); }; 
    g.fmap(f)(42); // 85 
+1

Когда вы объединяете две функции с 'fmap' в Haskell, вы получаете тот же результат, что и с' (.) '([См. Этот вопрос] (http://stackoverflow.com/questions/27883414/difference-between-function- состав-оператор и-FMAP)). И если вы его составляете ('fmap. Fmap'), он ведет себя как' (.). (.) '([См. Этот ответ] (http://stackoverflow.com/a/5822395)). Чтобы лучше понять, почему я пытался реализовать такой «fmap» в Javascript. Но оказалось, что это не имеет смысла, потому что 'fmap' (в сочетании с двумя функциями) и' comp' практически одинаковы в Javascript. – ftor

+0

Получил это сейчас - я добавил P.S. – FPstudent