按标题,我的意思是像Monad m => m (m a)
这样的类型。
当monad的结构很简单时,我很容易想到这种用法:
[[a]]
,这是一个多维列表
Maybe (Maybe a)
,这是一个伴随两个错误状态的类型
Either e (Either e a)
,与上面类似,但带有消息
Monoid m => (m,(m,a))
,这是一个作家monad,有两件事要重写
r -> r -> a
,这是一个读者monad,有两件事可供阅读
Identity (Identity a)
,它仍然是monad身份
Complex (Complex a)
,它是2 x 2矩阵
但是,如果我想到以下几种类型,这在我脑海中是一团糟:
ReadP (ReadP a)
?当ReadP
不是Read
的实例时为什么有用?
ReadPrec (ReadPrec a)
?像上面一样?
Monad m => Kleisli m a (Kleisli m a b)
?
IO (IO a)
!? 必须有用。很难考虑。
forall s. ST s (ST s a)
!?就像上面一样。
这类类型是否有实际用途?尤其是对于IO
人?
第二个想法,我可能需要随机选择一个IO
动作。那是IO (IO a)
的例子,它专注于输入。那么专注于产出呢?
答案 0 :(得分:2)
从某种意义上说,单子可以看作是函子,其中的层可以折叠。
如果const highchartsExporter = require('highcharts-export-server');
let promiseId = 0;
exports.generateAllCharts = (chartData, callback) => {
let allPromises = [];
let chartsLen = chartData.length;
highchartsExporter.logLevel(4);
highchartsExporter.initPool({
maxWorkers: 100,
initialWorkers: 50,
workLimit: 100,
queueSize: 50,
timeoutThreshold: 10000
});
if (!chartData || !chartsLen) {
highchartsExporter.killPool();
return callback({
code: '4',
msg: 'Please send chartdata'
});
}
for (let i = 0; i < chartsLen; i++) {
allPromises.push(
new Promise((resolve, reject) => {
exports.getPieChartImg(chartData[i], false, results => {
if (results.code !== '0') {
return reject(results);
}
return resolve(results);
});
})
);
}
Promise.all(allPromises)
.then(data => {
highchartsExporter.killPool();
let imagesObject = {
code: '0',
custImg: {}
};
data.forEach((image, index) => {
imagesObject.custImg['pc' + (index + 1)] = image.data;
imagesObject.custImg.promiseId = image.promiseId;
});
return callback(imagesObject);
})
.catch(err => callback({
code: '5',
msg: 'Error generating charts',
err
}));
};
exports.getPieChartImg = (seriesData, xOrLength, cb) => {
let chartOpts = {
colors: ['#7380D4', '#749FD4', '#74BFD4', '#74D4B6', '#99EBA8', '#FEE08B', '#FDAE61', '#F07346', '#E65433', '#C92D22'],
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
renderTo: 'container',
style: {
fontSize: '20px',
background: '#fffdcc'
},
width: 650,
},
credits: {
enabled: false
},
title: {
text: null,
},
tooltip: {
pointFormat: '{series.name}: {point.percentage:.1f}%'
},
legend: {
itemStyle: {
font: 'sans-serif',
fontWeight: 'bold',
fontSize: '13px'
},
useHTML: true,
layout: 'vertical',
align: 'right',
verticalAlign: 'middle',
labelFormatter: () => {
if (this.name[xOrLength] > 9) {
let words = this.name.split(/[\s]+/);
let numWordsPerLine = 1;
let str = [];
for (let word in words) {
if (parseInt(word) > 0 && parseInt(word) % numWordsPerLine == 0)
str.push('<br>');
str.push(words[word]);
}
let label = str.join(' ');
// Make legend text bold and red if most recent value is less than prior
if (this.name[1] > this.name[2]) {
return '<span style="font-weight:bold">' + label + '</span>';
} else {
return label;
}
} else {
return this.name;
}
}
},
plotOptions: {
pie: {
size: '85%',
allowPointSelect: true,
cursor: 'pointer',
showInLegend: true,
dataLabels: {
enabled: true,
allowOverlap: false,
distance: 10,
formatter: () => {
return undefined;
// if (parseFloat(this.percentage.toFixed(2)) > 0.35) {
// return '' + parseFloat(this.percentage).toFixed(2) + '%';
// }
},
padding: 5,
style: {
fontFamily: '\'Lato\', sans-serif',
// lineHeight: '18px',
fontWeight: 'normal',
fontSize: '18px'
}
}
},
series: {
stacking: 'normal',
dataLabels: {
enabled: true,
color: '#6f6f6f',
style: {
fontFamily: '\'Lato\', sans-serif',
// lineHeight: '18px',
fontWeight: 'normal',
fontSize: '18px'
},
format: '{point.percentage:.2f}'
},
pointWidth: 30,
cursor: 'pointer'
}
},
series: [{
name: "Value",
type: 'pie',
data: seriesData
}],
navigation: {
buttonOptions: {
enabled: false
}
},
};
let exportSettings = generateExportSettings(chartOpts, 'Stock');
return generateBase64Chart(exportSettings, 3, cb);
};
function generateExportSettings(chartOpts, constr) {
return {
type: 'png',
constr,
b64: true,
// async: false,
noDownload: true,
scale: 2,
options: chartOpts,
globalOptions: {
colors: ['#7380D4', '#749FD4', '#74BFD4', '#74D4B6', '#99EBA8', '#FEE08B', '#FDAE61', '#F07346', '#E65433', '#C92D22'],
lang: {
thousandsSep: ','
}
}
};
}
function generateBase64Chart(exportSettings, number, cb) {
// Perform an export
highchartsExporter.export(exportSettings, function(err, res) {
// The export result is now in res.
// If the output is not PDF or SVG, it will be base64 encoded (res.data).
// If the output is a PDF or SVG, it will contain a filename (res.filename).
if (err) {
return cb({
code: '1',
msg: 'Error in stock chart',
err,
exportSettings
});
}
promiseId++;
return cb({
code: '0',
msg: 'Success',
promiseId: promiseId,
data: 'data:image/png;base64,' + res.data,
});
// Kill the pool when we're done with it, and exit the application
// highchartsExporter.killPool();
// process.exit(1);
});
}
类的定义更像是类别理论的定义,则看起来像
Monad
将class Applicative m => Monad m where
return :: a -> m a
join :: m (m a) -> m a
与类型为fmap
的函数一起使用会导致类型为a -> m b
的函数。 m a -> m (m b)
用于从结果中消除一层monad。由于这是很常见的事情,因此可以定义一个函数来执行此操作。
join
如果仔细看,您会发现foo :: Monad m => (a -> m b) -> m a -> m b
foo f ma = join (fmap f ma)
为foo
,并且其参数已翻转。
>>=
由于foo = flip (>>=)
的使用量超过了>>=
的使用,因此类型类定义为
join
和class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
被定义为单独的函数
join
答案 1 :(得分:1)
没关系。
Monad之所以是Monad,是因为对于每个Monad ( Monad a )
,我们总能得到Monad a
。这种操作称为“连接”,它是“绑定”的替代操作,可以形成Monad的定义。 Haskell之所以使用“绑定”,是因为它在编写单子代码时非常有用:)
(join可以使用bind来实现,并可以通过join进行绑定-它们是等效的)
没关系
实际上是个小谎言,因为形成Monad ( Monad a )
的能力实际上也是使Monads成为monads的一部分。在某些操作中,Monad (Monad a)
是过渡表示。
完整答案是:是的,因为这样可以启用Monads。尽管Monad ( Monad a )
在列出某些Monad时可以具有额外的“域”含义;)