Нужна помощь в понимании того, что здесь делается

#f#

#f#

Вопрос:

Я читаю книгу «F # для разработчиков C #» Тао Лю. На странице 139 есть пример шаблона наблюдателя. Я не уверен, что Microsoft press или Tao позволят мне опубликовать полный список. Но вот часть, которую я пытаюсь обдумать.

 // subscribe to a notification function
member this.Subscribe notifyFunction = 
    let wrap f i = f i ; i
    notify <- wrap notifyFunction >> notify
 

в частности, let wrap f i = f i; i

Я знаю, что точка с запятой является разделителем для следующего оператора, поэтому i после точки с запятой само по себе указывает на то, что это возвращаемое значение Subscribe

Похоже, что wrap — это функция, которая принимает f и i в качестве аргументов. f является функцией, которая принимает один аргумент, и в определении wrap f вызывается с i в качестве аргумента.

В следующей строке wrap поставляется с одной составной функцией вместо двух аргументов.

Может кто-нибудь помочь мне понять это? Я посмотрел это в FSI и увидел следующее

 val wrap : f:('a -> unit) -> i:'a -> 'a
 

Мне кажется, что wrap у него есть два аргумента: один — это f функция a, в которой a является типом, полученным из использования, и ничего не возвращает, второй аргумент wrap имеет сам тип a, который снова выводится из использования, а wrap возвращает значение типа a .

Вся эта комбинация всего сбивает меня с толку. Может кто-нибудь дать мне простой способ понять это?

Ответ №1:

Ваш анализ wrap верен: это просто способ подтолкнуть некоторый побочный эффект к функции идентификации (имхо, немного пахнет)

Я думаю, что лучший способ понять эту функцию может быть:

 let wrap (action : 'a -> unit) : 'a -> 'a =
   fun a -> action a
            a
 

Следующая строка

 notify <- wrap notifyFunction >> notify
 

очень странно — это похоже x = x 1 на то, что он изменяет notify функцию (должна быть изменяемой переменной?) Для вызова notifyFunction , прежде чем она будет делать то, что делала раньше.

Не видя остальной части кода, я могу только предполагать: я думаю notify , начинается с того, что просто появляется что-то вроде

 let mutable notify = fun _ -> ()
 

Теперь всякий раз, когда вы впервые вызываете Subscripe действие f: 'a -> () , код будет меняться notify , чтобы быть эквивалентным этому:

 let notify a = 
   f a
   ()
 

Второй вызов с f' помощью даст вам нечто эквивалентное этому:

 let notify a = 
   f' a
   f a
   ()
 

и так далее…

Это будет работать, но, пожалуйста: не делайте ничего подобного — это нечитаемо, и это совсем не так, как вы должны обращаться с вещами в F#

Комментарии:

1. У вас довольно проницательные навыки. Вы получили объявление notify совершенно правильно

2. не нужно много навыков… все дело в типах 😉

3. вы согласны с ответом или есть что-то, чего вы не понимаете?

4. Wrap может использоваться для ведения журнала каждый раз, когда запускается уведомление или что-то подобное. Конечно, есть случаи, когда этого не следует делать таким образом, но я не думаю, что это изначально плохо.

5. да — но не пишите это так, чтобы вы не могли видеть, что он делает — в приведенном здесь примере это «волшебная» функция, но отступ скрыт неправильным наименованием и отсутствием подписи типа (всегда указывайте подписи для функций верхнего уровня и важных функций) -было бы понятнее, если бы warp здесь напрямую использовал функцию notifiyFunction и имел имя prepend или что-то в этом роде — я добавлю свой ответ, чтобы сделать это немного понятнее