我正在学习有关单调的信息。我阅读此wiki page 彻底到达 prograge> progragging 部分。我决定在C#中实施。
一个示例以两个简单的函数/方法开头,该函数/方法仅将整数值作为函数参数并返回另一个整数值。
然后,它描述了一个方案,其中需要在这些功能/方法中执行一些记录:
,但是假设我们正在调试程序,我们想向FOO和BAR添加记录消息。因此,我们更改类型:
foo : int -> int * string
bar : int -> int * string
以便两个功能都以应用程序为整数的结果返回元组,以及带有有关应用功能的信息的记录消息,以及所有先前应用的功能作为字符串。
然后,Wiki解释了不再可能使用此方法签名方法链的问题(因为输入和输出类型不是相同的)。
然后,该示例通过添加一个辅助函数/方法称为 bind
:
bind : int * string -> (int -> int * string) -> int * string
该方法是在两个参数中进行的:
- 一个元组(整数和日志消息字符串
- )该本身具有一个函数参数(类型整数)和元组的返回值。
...并返回元组。
关于此辅助功能/方法提供的解释非常困难,但实际上是有道理的:
bind
插入整数和字符串元组,然后将功能(例如 foo
)映射到整数到整数和字符串元组。它的输出是一个整数和字符串元组,这是将输入函数应用于输入整数和字符串元组中的整数的结果。这样,我们只需要编写样板代码即可从一次元组中提取整数, bind
。
。
然后结论:
现在我们恢复了一些合成性。例如:
绑定(绑定(x,s)bar)foo
因此,我已经在C#App:...中构建了所有这些内容:
public static void Run()
{
var resultOne = Bind(Bind(new(10, "Test"), BarWithLogging), FooWithLogging);
}
public static (int, string) FooWithLogging(int x)
{
return (x, "Log from Foo");
}
public static (int, string) BarWithLogging(int x)
{
return (x, "Log from Bar");
}
public static (int, string) Bind((int x, string s) tuple, Func<int, (int, string)> function)
{
return function(tuple.x);
}
...问题是我的记录功能是没有意义的。首先,我们必须将元组传递给 bind
函数/方法,并且元组包含一个日志消息。但是我认为应该从函数/方法和 bar
中创建日志消息(在我的情况下代码>)不是吗?如果是这样,这是否意味着我们只需要将一个虚拟日志消息元组传递给绑定功能?似乎答案是 no
,因为稍后,在该示例中,当我们想创建一个空日志时不必传递空日志消息时,有另一个步骤解决了情况。这意味着,如果我们想记录一个不是空的东西,那么我们会传递一条非空的消息(因此,非空消息将因此记录下来)。
...在这种情况下,我们如何将该日志消息值传递给 foo
函数?它仅接受整数!而且,由于它返回元组(具有日志消息值),它使我得出一个结论,即在 foo
函数中生成了实际日志消息;但这与我以前的结论相矛盾!
我想念什么?如何实现该单调的概念,以便我从记录功能中受益?
提前致谢。
I am learning about monads. I read this Wiki page thoroughly and reached the Program logging
section. I've decided to implement that in C#.
An example starts with two simple functions/methods, which just take an integer value as a function argument and return another integer value.
Then it describes a scenario where some logging needs to be performed inside these functions/methods:
But suppose we are debugging our program, and we would like to add logging messages to foo and bar. So we change the types as so:
foo : int -> int * string
bar : int -> int * string
So that both functions return a tuple, with the result of the application as the integer, and a logging message with information about the applied function and all the previously applied functions as the string.
Then Wiki explains the problem where with this method signature method chaining is no longer possible (because an input and output types are not the same).
Then, the example introduces a solution by adding a helper function/method called bind
:
bind : int * string -> (int -> int * string) -> int * string
What this one does is that it takes in two arguments:
- a tuple (of an integer and a log message string)
- a function that itself has a function argument (of type integer) and a return value of a tuple.
...and returns a tuple.
There is a really hard to follow explanation provided about this helper function/method, but it actually makes sense:
bind
takes in an integer and string tuple, then takes in a function (like foo
) that maps from an integer to an integer and string tuple. Its output is an integer and string tuple, which is the result of applying the input function to the integer within the input integer and string tuple. In this way, we only need to write boilerplate code to extract the integer from the tuple once, in bind
.
And then it concludes:
Now we have regained some composability. For example:
bind (bind (x,s) bar) foo
So, I've built all of this in C# app:
public static void Run()
{
var resultOne = Bind(Bind(new(10, "Test"), BarWithLogging), FooWithLogging);
}
public static (int, string) FooWithLogging(int x)
{
return (x, "Log from Foo");
}
public static (int, string) BarWithLogging(int x)
{
return (x, "Log from Bar");
}
public static (int, string) Bind((int x, string s) tuple, Func<int, (int, string)> function)
{
return function(tuple.x);
}
...and the problem is that my logging functionality just doesn't make sense. First of all, we have to pass a tuple to the bind
function/method, and that tuple contains a log message; but I thought that log message should be created from the function/method foo
and bar
(in my case FooWithLogging
and BarWithLogging
) shouldn't it? If so, then does it mean we just have to pass a dummy log message tuple part to the bind function? It seems the answer is no
because later, in that example there is another step that addresses the case when we'd like to not have to pass an empty log message when we want to create an empty log; which implies that if we want to log something that is not empty then we pass a non-empty message (and that non-empty message will be logged as a result).
...in that case how can we pass that log message value to the foo
function? It accepts an integer only! And since it returns a tuple (that has the log message value) it leads me to a conclusion that the actual log message is generated within the foo
function; but that contradicts my previous conclusion!
What am I missing? How to implement that monad concept so that I get a benefit of logging functionality?
Thanks in advance.
发布评论
评论(1)
Wikipedia文章部分看起来有些混乱。通常,您会使用状态单元进行类似的东西,但是Wikipedia文章的那一部分使用本质上是'tuple functor的东西,只有当'额外'数据形成a
幸运的是,字符串引起了串联的单调,这也是为什么Wikipedia文章可以通过Monadic
return> return
来摆脱定义(x,“”)
:空的原因。字符串(“”
)是字符串串联monoid的身份。这也意味着,为了能够通过绑定方法同时携带现有和新的“日志”字符串,您需要使用单型二进制操作。对于字符串串联,就是这样:串联。
因此,
bind
函数应该看起来像这样:我没有尝试编译此功能,因此可能会有错字,但我希望它可以说明这一点。
另一个选项是以相反的顺序连接:
newtuple.snd + tuple.snd
。由于看起来您正在使用C#,因此您可能会发现我的文章系列有用。我将在几周内到达州单元,但是今天您可以偷看状态函数。
That Wikipedia article section looks a bit confusing. Normally, you'd use the State monad for something like that, but that part of the Wikipedia article instead uses what is essentially 'the' tuple functor, which is only a monad if the 'extra' data forms a monoid.
Strings, fortunately, give rise to a monoid over concatenation, which is also why the Wikipedia article can get away with defining
(x, "")
as monadicreturn
: The empty string (""
) is the identity for the string concatenation monoid.This also means that in order to be able to carry both the existing and the new 'log' string through the bind method, you'll need to use the monoidal binary operation. For string concatenation, that is exactly that: Concatenation.
So the
bind
function should look like this:I haven't tried to compile this, so it's possible that there's a typo, but I hope it gets the point across.
Another option is to concatenate in the opposite order:
newTuple.Snd + tuple.Snd
.Since it looks like you are working with C# you may find my articles series on monads useful. I'll get to the State monad in a couple of weeks, but already today you can take a sneak peek at the State functor.