Examples of NDIToolbox plugins
Now that you've read the basics on developing plugins for NDIToolbox, it's time to see some basic examples.
We've already seen example code for local plugins, now let's look at one way to keep code on the server. Consider a
plugin designed to detect edges in a 2D image plot. SciPy
has several routines of interest for edge detection, but suppose we've decided to use the excellent
scikits-image image processing add-on for SciPy. This makes things slightly
more complicated because scikits-image requires compiling some C code on installation; this can be problematic for
Windows users that don't have Visual Studio installed. We've therefore decided to keep most of the code on a server
we control so we can operate in a well-controlled environment, and have the plugin phone in for calculations. This
also gives some protection for any intellectual property we might develop as the toolkit evolves. It's also a path
to commercialization by e.g. selling subscriptions to the toolkit's calculations and requiring an active login in
our toolkit's config
for access.
After looking at JSON and XML-RPC, we've decided to go with Python's SimpleXMLRPCServer. There are several different edge detection algorithms we could employ, and since they'd all be run on the server anyway we've decided to just run one edge detector server that can provide all the algorithms we implement. The idea will be to expose an API that offers several different edge detection algorithms; we could write a single NDIToolbox plugin that allows the user to select the algorithm on the fly but for the purposes of this demonstration we'll write one plugin per algorithm. In this case, we'll implement the Sobel and Canny methods.
On the server side, we simply need to tweak the example code shown in SimpleXMLRPCServer to provide the methods we need:
#!/usr/bin/env python """server.py - example of hosting NumPy functions on a server via XML-RPC Chris R. Coughlin (TRI/Austin, Inc.) """ # A modified version of the Python example code for SimpleXMLRPCServer # http://docs.python.org/library/simplexmlrpcserver.html#module-SimpleXMLRPCServer # Demonstrates one way to move large NumPy arrays between a client and server - # client sends NumPy array, server performs calculations and returns results from SimpleXMLRPCServer import SimpleXMLRPCServer from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler from skimage import filter import numpy as np class RequestHandler(SimpleXMLRPCRequestHandler): rpc_paths = ('/edge_detector',) # Create server server = SimpleXMLRPCServer(("", 8000), requestHandler=RequestHandler) server.register_introspection_functions() def sobel_edges(arr_as_list): """Applies the Sobel operator to the provided data, returns the edges detected. Use numpy.array(returned_data) to produce NumPy array.""" an_array = np.array(arr_as_list) edges = filter.sobel(an_array) return edges.tolist() server.register_function(sobel_edges, 'sobel_edges') def canny_edges(arr_as_list, std_dev="1.0", low_t="0.1", high_t="0.2"): """Applies the Canny algorithm to the provided data, returns the edges detected. Use numpy.array(returned_data) to produce NumPy array.""" an_array = np.array(arr_as_list) sigma = float(std_dev) low_threshold = float(low_t) high_threshold = float(high_t) edges = filter.canny(an_array, sigma, low_threshold, high_threshold) return edges.tolist() server.register_function(canny_edges, 'canny_edges') # Run the server's main loop server.serve_forever()
The server starts up and stays running; listening for client connections on port 8000. A client sends a NumPy array (the current data set in the image plot) converted to a standard Python list; the server converts back to a NumPy array and returns the edges. On the client side, the plugin will then re-convert from the Python list to a NumPy array and NDIToolbox refreshes the image plot with the results.
Looking at the scikits-image Sobel documentation, no parameters are necessary to run the filter. So a simple Sobel Edge Detection plugin for NDIToolbox might look like this:
"""sobel_edge_detection_plugin.py - simple A7117 plugin that demonstrates XML-RPC communication by returning edges found in an image Chris R. Coughlin (TRI/Austin, Inc.) """ __author__ = 'Chris R. Coughlin' from models.abstractplugin import TRIPlugin import numpy as np import xmlrpclib class SobelPlugin(TRIPlugin): """Returns absolute magnitude Sobel to find edges in an image http://en.wikipedia.org/wiki/Sobel_operator""" name = "Sobel Edge Detection" description = "Applies the Sobel operator to the current data to detect edges" authors = "Chris R. Coughlin (TRI/Austin, Inc.)" version = "1.0" url = "www.tri-austin.com" copyright = "Copyright (C) 2012 TRI/Austin, Inc. All rights reserved." def __init__(self): super(SobelPlugin, self).__init__(self.name, self.description, self.authors, self.url, self.copyright) self.config = {'server_url':'http://<IP_ADDRESS>:8000/edge_detector'} self.srvr = xmlrpclib.ServerProxy(self.config['server_url']) def run(self): """Executes the plugin - returns a new NumPy array with edges detected in original data""" if self._data is not None: # Support "batch mode" - may be passed multiple datasets as a dict if hasattr(self._data, "keys"): for dataset in self._data: # Execute plugin on every dataset current_data = self._data[dataset].astype(np.float64).tolist() self._data[dataset] = self.srvr.sobel_edges(current_data) else: # Passed a single dataset dataset = self._data.astype(np.float64).tolist() self._data = self.srvr.sobel_edges(dataset)
The only configuration option we require in this case is a pointer to the edge detection server's URL; you could hard-wire this into the plugin to streamline the plugin's operation if desired. Here's an example of the Sobel plugin - before and after execution:
Looking at the Canny edge detector API,
we can specify thresholds and a number of standard deviations. In this case, we'll add each of these parameters to
the plugin's config
dict so that the user can provide arguments to the server:
"""canny_edge_detection_plugin.py - simple A7117 plugin that demonstrates XML-RPC communication by returning edges found in an image Chris R. Coughlin (TRI/Austin, Inc.) """ __author__ = 'Chris R. Coughlin' from models.abstractplugin import TRIPlugin import numpy as np import xmlrpclib class CannyPlugin(TRIPlugin): """Detects edges in an image with the Canny algorithm http://en.wikipedia.org/wiki/Canny_edge_detector """ name = "Canny Edge Detection" description = "Applies the Canny algorithm to the current data to detect edges" authors = "Chris R. Coughlin (TRI/Austin, Inc.)" version = "1.0" url = "www.tri-austin.com" copyright = "Copyright (C) 2012 TRI/Austin, Inc. All rights reserved." def __init__(self): super(CannyPlugin, self).__init__(self.name, self.description, self.authors, self.url, self.copyright) self.config = {'sigmas':1, 'low_threshold':0.1, 'high_threshold':0.2, 'server_url':'http://<IP_ADDRESS>:8000/edge_detector'} self.srvr = xmlrpclib.ServerProxy(self.config['server_url']) def run(self): """Executes the plugin - returns a new NumPy array with edges detected in original data""" if self._data is not None: # Support "batch mode" - may be passed multiple datasets as a dict if hasattr(self._data, "keys"): for dataset in self._data: self.srvr.canny_edges(self._data[dataset].astype(np.float64).tolist(), self.config['sigmas'], self.config['low_threshold'], self.config['high_threshold']) else: # Passed a single dataset self._data = self.srvr.canny_edges(self._data.astype(np.float64).tolist(), self.config['sigmas'], self.config['low_threshold'], self.config['high_threshold'])
In this case the results of the Canny edge detection aren't as nice as the Sobel; but of course the user can revert to their original data and try experimenting with the Canny parameters to optimize the results.
Once we've set up an edge detection server, we'd update the source code for both plugins to change the
server_url
key in the config
dict to point to the server. Since both plugins consist of a
single file, installation is as simple as asking the user to copy both .py
files to their plugins
folder. Assuming you'd prefer to make things easier for your users however, you'd probably want to make a proper
NDIToolbox plugin archive so that NDIToolbox can perform the plugin installation automatically. To do that for the
Sobel plugin for example, create a new ZIP sobel_edge_detection_plugin.zip
and add
sobel_edge_detection_plugin.py
and a README to the archive such as the following.
# README Contents Sobel Edge Detection - returns detected edges in image plot data Chris R. Coughlin (TRI/Austin, Inc.) www.tri-austin.com Uses the Sobel operation (http://en.wikipedia.org/wiki/Sobel_operator) to detect edges in 2D data.
Once we've created the sobel_edge_detection_plugin.zip
file, we can directly provide the archive to the
user and have them perform a local installation. The other option is to host the archive on a server and allow the
user to perform a remote installation such as the Linux user shown below.
As the first example of using other programming languages in NDIToolbox plugins, consider the case that you have pre-existing data analysis code written in Java and rather than attempt to port to Python you'd like a relatively easy way to re-use your existing Java code.
Suppose that we have written a standard Java library ToolboxDemoLib.jar
with a BasicStats
class that we'd like to call from Python:
package stats; import java.util.ArrayList; import java.util.List; /** * * @author ccoughlin */ public class BasicStats { public static double calcMin(List<double> dataList) { double minVal = dataList.get(0); for (double el:dataList) { if (el < minVal) { minVal = el; } } return minVal; } public static double calcMax(List<double> dataList) { double maxVal = dataList.get(0); for (double el:dataList) { if (el > maxVal) { maxVal = el; } } return maxVal; } public static List<double> normalizedData(List<double> dataList) { double maxValue = calcMax(dataList); List<double> normData = new ArrayList<>(dataList.size()); for (double el:dataList) { normData.add(el/maxValue); } return normData; } }
The primary difficulty in using other programming languages with NDIToolbox is in dealing with NumPy arrays.
Although
numerous bridges between Python and other languages exist, for the most part NumPy arrays are not directly
supported. The most straightforward way to deal with the data
arrays your plugin will receive and send
is to convert to and from standard Python lists
, which are well supported.
Going back to our scenario, there are several ways to connect Python and Java code. We've decided to go with
Jython in this case as it is an active and fairly well-supported project.
Jython also allows us to make more use of existing NDIToolbox plugin code: as a nearly-compatible Python
implementation running on the JVM, we can use large portions of the existing .py
we've used elsewhere
in this document.
There are two basic ways to use Jython in this scenario: embed Python code in an existing Java application, or load
Java code into a Python application. If your Java code is a full-blown application, you would probably want to embed
Jython in the application to have it interact with the plugin front-end. In this case however, our Java code is
simple enough that it's more convenient to call it from a Jython instance. To do this, build the project as usual to
get ToolboxDemoLib.jar
. Following the
Jython installation
instructions, build a self-contained jython.jar
and copy ToolboxDemoLib.jar
to
this folder. The Jython interpreter will then be able to import your Java library with the following:
import sys sys.path.append("ToolboxDemoLib.jar") from stats import BasicStats
As it stands, running the above code in Jython would make our Java code available to any subsequent code, but we
still need to connect Jython with Python. Again, there are several ways to accomplish this but one way is to recycle
the XML-RPC code we used earlier: if we create a server.py
file to run in Jython, it can then respond
to client.py
XML-RPC requests from Python. Since Jython is mostly compatible with Python this code will
look very similar to the code we used previously to demonstrate plugin communications with a server backend.
"""server.py - example of hosting Java functions on a server via XML-RPC Chris R. Coughlin (TRI/Austin, Inc.) """ import sys sys.path.append("ToolboxDemoLib.jar") from stats import BasicStats from SimpleXMLRPCServer import SimpleXMLRPCServer from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler class RequestHandler(SimpleXMLRPCRequestHandler): rpc_paths = ('/basic_stats',) server = SimpleXMLRPCServer(("", 8000), requestHandler=RequestHandler) server.register_introspection_functions() # Not strictly necessary here since all the methods are static, # provided to demonstrate instantiation of Java classes calculator = BasicStats() def min(arr): """Returns the minimum of an array as a double""" converted_arr = [float(el) for el in arr] return BasicStats.calcMin(converted_arr) server.register_function(min, 'min') def min2d(arr): """Returns the minimum of a 2D array as a double""" min_val = sys.float_info.max for row in arr: row_min = min(row) if row_min < min_val: min_val = row_min return min_val server.register_function(min2d, 'min2d') def max(arr): """Returns the maximum of an array as a double""" converted_arr = [float(el) for el in arr] return BasicStats.calcMax(converted_arr) server.register_function(max, 'max') def max2d(arr): """Returns the maximum of a 2D array as a double""" max_val = sys.float_info.min for row in arr: row_min = max(row) if row_min > max_val: max_val = row_min return max_val server.register_function(max2d, 'max2d') def normalize(arr): """Returns the normalized input array. Elements are converted to floating point if required.""" converted_arr = [float(el) for el in arr] return list(calculator.normalizedData(converted_arr)) server.register_function(normalize, 'normalize') def normalize2d(arr): """Returns the normalized 2D input array. Elements are converted to floating point if required.""" normalized_data = [] for row in arr: normalized_row = normalize(row) normalized_data.append(normalized_row) return normalized_data server.register_function(normalize2d, 'normalize2d') # Run the server's main loop server.serve_forever()
To run this code we simply run jython.jar
as a standard Java JAR, and provide it with the name of the
script, e.g. java -jar jython.jar server.py
. Note that Jython does a lot of the work for us in
conversion, e.g. automatically handling the conversion between Python's lists
and our Java code's
ArrayList
. We have had to make a few adjustments to our original server code since NumPy does not run
on Jython, most notably that we now have to convert to and from NumPy arrays on a row-by-row basis.
Similarly, the plugin's frontend code will look familiar:
"""jython_normalize_plugin.py - simple A7117 plugin that demonstrates XML-RPC communication with Java code on the server Chris R. Coughlin (TRI/Austin, Inc.) """ __author__ = 'Chris R. Coughlin' from models.abstractplugin import TRIPlugin import numpy as np import xmlrpclib class JythonNormalizePlugin(TRIPlugin): """Normalizes the data""" name = "Jython Normalize" # Name in the Plugin menu description = "Demonstrates communication with a Java backend" authors = "Chris R. Coughlin (TRI/Austin, Inc.)" version = "1.0" url = "www.tri-austin.com" copyright = "Copyright (C) 2012 TRI/Austin, Inc. All rights reserved." def __init__(self): super(JythonNormalizePlugin, self).__init__(self.name, self.description, self.authors, self.url, self.copyright) self.config = {'server_url':'http://<IP_ADDRESS>:8000/basic_stats'} self.srvr = xmlrpclib.ServerProxy(self.config['server_url']) def run(self): """Executes the plugin - if data are not None they are normalized against the largest single element in the array.""" if self._data is not None: # Support "batch mode" - may be passed multiple datasets as a dict if hasattr(self._data, "keys"): for dataset in self._data: current_data = self._data[dataset].tolist() if self._data[dataset].ndim == 1: self._data[dataset] = np.array(self.srvr.normalize(current_data)) elif self._data.ndim == 2: self._data[dataset] = np.array(self.srvr.normalize2d(current_data)) else: # Passed a single dataset current_data = self._data.tolist() if self._data.ndim == 1: self._data = np.array(self.srvr.normalize(current_data)) elif self._data.ndim == 2: self._data = np.array(self.srvr.normalize2d(current_data))
The only major difference in this plugin compared to the previous XML-RPC example is that in this case the plugin defaults to assuming the Jython XML-RPC server is running on the local machine. You can of course run the Jython server on a different machine, and in fact this may be preferable if you would prefer to work in a known good environment and/or don't want to require users to have Java installed on their machine.
As an example of how to write extensions in C++ for Python, let's suppose that the Java library shown above was instead a C++ library, and we would like to use it from Python. Rather than going the XML-RPC route, we'd like to compile a C++ extension that Python can call directly. Extending Python with C or C++ code is relatively straightforward on POSIX (OS X, Linux, FreeBSD, etc.) platforms, but can be tricky on Windows machines. Quoting from the official Python documentation on the process:
In general, you should use the same compiler and version of compiler to build your extension as was used to build Python itself. If you are using a different compiler, you may need to build Python from source in order for your extension to function properly. In this example, we will demonstrate using Boost.Python to build a C++ extension for Windows machines using Visual Studio 2008 and Python 2.7. If you expect your C++ extension to be used on other operating systems, you will need to become familiar with the C/C++ compiler used on that OS (e.g. GCC or LLVM). For an example of how to use SWIG to build a C++ extension on Linux machines with GCC, NDIToolbox's developer has provided an example on his website that isn't specific to NDIToolbox but may still prove useful.
basicstats.h#pragma once #include <vector> class BasicStats { public: BasicStats(void) {}; virtual ~BasicStats(void) {}; double calcMin(const std::vector<double>& dataList); double calcMax(const std::vector<double>& dataList); std::vector<double> normalizedData(const std::vector<double>& dataList); std::vector<std::vector<double>> normalizedData(const std::vector<std::vector<double>>& dataList); };basicstats.cpp
#include "StdAfx.h" #include "BasicStats.h" double BasicStats::calcMin(const std::vector<double>& dataList) { double minVal = dataList[0]; for (std::vector<double>::const_iterator iter=dataList.begin(); iter!=dataList.end(); ++iter) { if (*iter < minVal) { minVal = *iter; } } return minVal; } double BasicStats::calcMax(const std::vector<double>& dataList) { double maxVal = dataList[0]; for (std::vector<double>::const_iterator iter=dataList.begin(); iter!=dataList.end(); ++iter) { if (*iter > maxVal) { maxVal = *iter; } } return maxVal; } std::vector<double> BasicStats::normalizedData(const std::vector<double> &dataList) { double maxValue = calcMax(dataList); std::vector<double> normData; for (std::vector<double>::const_iterator iter=dataList.begin(); iter!=dataList.end(); ++iter) { normData.push_back(*iter/maxValue); } return normData; } std::vector<std::vector<double>> BasicStats::normalizedData(const std::vector<std::vector<double>> &dataList) { std::vector<std::vector<double>> normData; for (std::vector<std::vector<double>>::const_iterator iter=dataList.begin(); iter!=dataList.end(); ++iter) { std::vector<double> normalizedRow = normalizedData(*iter); normData.push_back(normalizedRow); } return normData; }
One slight difference here from the previous Java code - we've included a method that handles 2D data. In the Java example we handled this in the Python code-you can use either approach based on personal preferences, benchmarking, etc.
Although you can use Boost.Python from within the Visual Studio IDE, it's actually easier to compile the extension using Boost.Build from the command line, so that's the approach we'll use here. Quoting from the Boost.Python documentation:
Begin by downloading the latest version of Boost, and extract it to a folder of your choice. Following the Getting Started On Windows guide, start by building the Boost lib by opening a command prompt in the folder you've extracted the Boost source to and issue the following commands:
bootstrap .\b2
Strictly speaking this step isn't necessary since it builds all the components of Boost that require compilation (i.e., not just Boost.Python), but it doesn't take very long and it's a good test to ensure that your compiler and Boost download are set up properly.
Looking at the
Boost.Python
documentation,
the next step is in creating a config file that configures the Boost
bjam tool's compilation for your setup. This
file is a simple text file and in our case we really only need it to a) configure our compiler and b) set the
version of Python we're using. In our $HOME
folder (e.g. c:\users\chris
in Windows 7 or
/home/chris
under Linux), we'd create a file named user-config.jam
that looks like the
following:
# MSVC configuration using msvc : 9.0 ; # Python configuration using python : 2.7 : C:/Python27 ;
Although the Boost.Python documentation recommends putting the bjam tool somewhere in your $PATH
so it
can be found on the command-line, it's just as easy to create a batch file bjam.bat
that points to the
bjam executable. A batch file is also a good choice because we need to set an environment variable
BOOST_BUILD_PATH
prior to compilation; putting it in the batch file saves trying to remember to set it
every time we want to recompile. Set BOOST_BUILD_PATH
to the folder that you've extracted Boost to; the
bjam
tool is normally in the root of this folder. For example, if you have extracted the Boost source
code to C:\Users\CRC\src\cxx\boost_1_50_0
, your bjam.bat
file would look like this:
@echo off set BOOST_BUILD_PATH=C:\Users\CRC\src\cxx\boost_1_50_0\ C:\Users\CRC\src\cxx\boost_1_50_0\bjam
Save the bjam.bat
file to the folder you're using for your toolbox source code (i.e., where your C++
extension files are located). Next, we create a Jamroot file (similar to a Makefile) that details how our project is
built. This file is also stored in the toolbox source code root folder; an example for this particular project
basicstats
is shown below.
# Copyright David Abrahams 2006. Distributed under the Boost # Software License, Version 1.0. (See accompanying # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) import python ; if ! [ python.configured ] { ECHO "notice: no Python configured in user-config.jam" ; ECHO "notice: will use default configuration" ; using python ; } # Specify the path to the Boost project. If you move this project, # adjust this path to refer to the Boost root directory. use-project boost : C:/Users/CRC/src/cxx/boost_1_50_0 ; # Set up the project-wide requirements that everything uses the # boost_python library from the project whose global ID is # /boost/python. project : requirements <library>/boost/python//boost_python ; # Declare the three extension modules. You can specify multiple # source files after the colon separated by spaces. python-extension basicstats_ext : basicstats.cpp ; # Put the extension and Boost.Python DLL in the current directory, so # that running script by hand works. install convenient_copy : basicstats_ext : <install-dependencies>on <install-type>SHARED_LIB <install-type>PYTHON_EXTENSION <location>. ; # A little "rule" (function) to clean up the syntax of declaring tests # of these extension modules. local rule run-test ( test-name : sources + ) { import testing ; testing.make-test run-pyd : $(sources) : : $(test-name) ; } # Declare test targets run-test basicstats : basicstats_ext basicstats.py ;
If you're just building a basic C++ extension, you can copy-paste this example as-is with a few modifications. In
particular, we're calling our module basicstats
so you'll want to edit your Jamroot if you're using a
different name (e.g. replace "basicstats" in the above with your project name).
That's it for defining the basic project; you can find another example of setting up a project from Boost.Python's
Hello, World!
example. Next, our original C++ code needs to be modified slightly - as it stands right now it expects to be dealing
with conventional C++ std::vector<double>
containers, but our code will need to handle Python
lists
instead. The most straightforward way of dealing with this change is by changing the C++ code to
use Boost.Python's boost::python::list
container. Here's the updated version.
#pragma once #include <vector> #include <boost/python.hpp> #include <boost/python/module.hpp> #include <boost/python/def.hpp> class BasicStats { public: BasicStats(void) {}; virtual ~BasicStats(void) {}; double calcMin(boost::python::list& dataList); double calcMax(boost::python::list& dataList); boost::python::list normalizedData(boost::python::list& dataList); boost::python::list normalized2DData(boost::python::list& dataList); };basicstats.cpp
#include "BasicStats.h" double BasicStats::calcMin(boost::python::list& dataList) { double minVal = boost::python::extract<double>(dataList[0]); for (int iter=0; iter<len(dataList); ++iter) { double el_value = boost::python::extract<double>(dataList[iter]); if (el_value < minVal) { minVal = el_value; } } return minVal; } double BasicStats::calcMax(boost::python::list& dataList) { double maxVal = boost::python::extract<double>(dataList[0]); for (int iter=0; iter<len(dataList); ++iter) { double el_value = boost::python::extract<double>(dataList[iter]); if (el_value > maxVal) { maxVal = el_value; } } return maxVal; } boost::python::list BasicStats::normalizedData(boost::python::list& dataList) { double maxValue = calcMax(dataList); boost::python::list normData; for (int iter=0; iter<len(dataList); ++iter) { double el_value = boost::python::extract<double>(dataList[iter]); normData.append(el_value/maxValue); } return normData; } boost::python::list BasicStats::normalized2DData(boost::python::list& dataList) { boost::python::list normData; for (int iter=0; iter<len(dataList); ++iter) { boost::python::list normalizedRow = normalizedData(boost::python::list(dataList[iter])); normData.append(normalizedRow); } return normData; }
One thing to note in this code is the normalized2DData
method we're using to normalize 2D arrays, in
particular the creation of a boost::python::list
to send to the 1D normalizedData
method.
To handle Python's dynamic nature, Boost.Python treats everything as a generic Python object so our code needs to
create a Python list
to pass to normalizedData
.
If you'd prefer not to convert your code to use lists (e.g. if you expect to continue using it in other applications), you can of course write an adapter class to convert to and from your code. Consult the Boost.Python documentation for details on converting between C++ and Python.
Now that the C++ data analysis library has been modified to work with Python, the only thing left to do is to define
our library's Python API. Add the following to basicstats.cpp
:
// Demo of exposed function char const* version() { return "BasicStats v0.1.1"; } BOOST_PYTHON_MODULE(basicstats_ext) { using namespace boost::python; def("version", version); class_<BasicStats>("BasicStats") .def("calcMin", &BasicStats::calcMin) .def("calcMax", &BasicStats::calcMax) .def("normalize", &BasicStats::normalizedData) .def("normalize2d", &BasicStats::normalized2DData) ; }
This addition creates the API that Python will see from our extension library: a class BasicStats
with
methods calcMin
, calcMax
, normalizedData
, and normalized2DData
;
and a function version
that we've added to illustrate how to expose standalone C++ functions in the
API.
This completes the project so the only thing left to do is run bjam
in our toolkit's source code folder
to compile our new Python module basicstats_ext.pyd
(basically a renamed .DLL). You should see output
similar to the following if all goes well.
C:\Users\CRC\src\cxx\toolbox_demo>bjam ...patience... ...patience... ...found 1715 targets... ...updating 7 targets... compile-c-c++ bin\msvc-9.0\debug\threading-multi\basicstats.obj basicstats.cpp msvc.link.dll bin\msvc-9.0\debug\threading-multi\basicstats_ext.pyd Creating library bin\msvc-9.0\debug\threading-multi\basicstats_ext.lib and object bin\msvc-9.0\debug\threading-multi\basicstats_ext.exp msvc.manifest.dll bin\msvc-9.0\debug\threading-multi\basicstats_ext.pyd common.copy basicstats_ext.pyd bin\msvc-9.0\debug\threading-multi\basicstats_ext.pyd 1 file(s) copied. capture-output bin\basicstats.test\msvc-9.0\debug\threading-multi\basicstats 1 file(s) copied. **passed** bin\basicstats.test\msvc-9.0\debug\threading-multi\basicstats.test ...updated 7 targets...
To see the new C++ module in action, you can create a simple Python script to call it:
import basicstats_ext # Making calls to standalone functions print("Requesting version info...{0}".format(basicstats_ext.version())) # Create sample data to test-drive the calculations demo_data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # Instantiating our C++ BasicStats class calculator = basicstats_ext.BasicStats() print("Min value={0}".format(calculator.calcMin(demo_data))) print("Max value={0}".format(calculator.calcMax(demo_data))) # Receiving a list print("Normalized data:\n") normalized_data = calculator.normalize(demo_data) print(normalized_data)
To create an NDIToolbox plugin from this new C++ extension, copy basicstats_ext.pyd
and the
newly-created Boost.Python support DLL boost_python-vc90-mt-gd-1_50.dll
(remembering that the names of
your extension files may be different) to the NDIToolbox plugins folder along with your plugin's Python front-end.
Here's a sample plugin that uses the BasicStats extension to normalize NumPy arrays.
"""cpp_normalize_plugin.py - simple A7117 plugin that demonstrates using C++ to extend Python Chris R. Coughlin (TRI/Austin, Inc.) """ __author__ = 'Chris R. Coughlin' from models.abstractplugin import TRIPlugin import numpy as np import xmlrpclib import basicstats_ext class CPPNormalizePlugin(TRIPlugin): """Normalizes the data""" name = "C++-Based Normalize" # Name in the Plugin menu description = "Demonstrates writing C++ code to extend NDIToolbox plugins" authors = "Chris R. Coughlin (TRI/Austin, Inc.)" version = "1.0" url = "www.tri-austin.com" copyright = "Copyright (C) 2012 TRI/Austin, Inc. All rights reserved." def __init__(self): super(CPPNormalizePlugin, self).__init__(self.name, self.description, self.authors, self.url, self.copyright) def run(self): """Executes the plugin - if data are not None they are normalized against the largest single element in the array.""" if self._data is not None: calculator = basicstats_ext.BasicStats() # Support "batch mode" - may be passed multiple datasets as a dict if hasattr(self._data, "keys"): for dataset in self._data: raw_data = self._data[dataset].astype(np.float).tolist() if self._data[dataset].ndim == 1: self._data[dataset] = np.array(calculator.normalize(raw_data)) elif self._data[dataset].ndim == 2: self._data[dataset] = np.array(calculator.normalize2d(raw_data)) else: # Passed a single dataset raw_data = self._data.astype(np.float).tolist() if self._data.ndim == 1: self._data = np.array(calculator.normalize(raw_data)) elif self._data.ndim == 2: self._data = np.array(calculator.normalize2d(raw_data))
If you plan on distributing your plugin as an archive, you'll need
to create a sub-folder to place the DLL and .PYD file. Note that you may then also have to adjust your paths to
compensate. Remember also that since you are distributing compiled code you may also need to consider your users'
operating system, Python version, etc. and ship multiple versions of your extension. You may also wish to collect
some basic information about the system your plugin is running on-operating system and
version of Python in particular-and use this information to tailor your plugin's
imports. NDIToolbox's mainmodel.py
module also contains some convenience functions that help to narrow
down which version of Windows is being used:
is_win7() is_winvista() is_winxp() is_winxp64() is_win2k()
The platform module in the Python Standard Library can
provide additional details, e.g. platform.python_compiler()
will identify the compiler used to build
the Python interpreter. If you need to ship multiple compiled extensions, you can then use this information to
determine which extension to import, for example:
import platform platform_name = platform.system() if platform_name == "Windows": import basicstats_win32_ext elif platform_name == "Linux": import basicstats_linux_ext # etc.


Mobile Eats
Lenny's Hot Dog Stand
Welcome to the page for Lenny's Hot Dog stand. Hosted by Mobile Eats.
Copyright © Mobile Eats 2017


{% extends "core/base.html" %} {% load bootstrap champion %} {% block content %}
| Date | Game type | Champion | KDA | CS |
|---|---|---|---|---|
| {{ match.created }} | {{ match.game_type }} | {% icon_for_champion match.champion_name %} | {{ match.kills }} / {{ match.deaths }} / {{ match.assists }} | {{ match.minions }} |


Download medor
Patients are looking for doctors like you
Download medor
Edit your profile from anywhere, effortlessly
Easily add or modify your details.
Add information that matters to your patients timings, fees, new services, and much more.
Keep all your information up to date, with ease.
Create your free Medor profile in 3 simple steps
Register or find yourself on medor.club
Enter your name, email id, mobile number and clinic or establishment name.
Add your profile information
Fill details about you and your practice including your medical registration, fees, timings and more.
Help us verify your details
Complete our a simple verification process online, and go live!
Download app
Download app
Learn With My Website
Learn With My Website
Learn With My Website
A perfect app that lets you manage and grow your practice.
quick link
About us
Contact us
Blog
Terms & Conditions
Privacy Policy
for patients
medor Profile
for Doctor
Social
Facebook
Linkedin
Youtube
copyright
©
2020 medor.club


Full stack developer with a passion for gaming and animations. Strong with the MERN stack and ready to take on any project. Contact me with any inquiries.
HTML
CSS
JavaScript
jQuery
Mongo
Express
React
Node
This app is a language interpretation and recognition interface. Liri will be a command line node app that takes in parameters and gives you back data
Constructor Word Guess is a command line node app similar to Hangman.
This app is an Amazon-like storefront using MySQL. The app will take in orders from customers and deplete stock from the store's inventory.
OSG (open source games) allows you to play a single player trivia game. It incorporates a page where the user is allowed to submit their own trivia question, which then has to be approved by the admins.


Design
Mix is the platform that brings together powerful design features you already love and a more efficient workflow to boot.
Collaborate
Team up to move design work forward, faster. Figma removes all the bad parts of collaboration and gets everyone on the same page literally.
Deploy
Deploy and scale seamlessly, Build more and spend less time managing your workflow.


It’s been a good three months since my last instalment and a lot has happened. A notable theme last time was that of indecision and the slightly uneasy feeling of living between two highly positive parts of the world. That feeling still remains, although it has manifested itself under new grounds.
Towards the start of February I went back home to see family, friends, and old places. I won’t go into the details but the trip was immensely helpful - one of its purposes was to allow me to compare the two lives side by side, to see what I preferred looking long-term. In short, it was a lovely visit, mostly because it was a nourishing taste of stability and familiarity, with a taste of gimpery. And gigs. Those too.
The pressing issue now is that of what to do next, and how I should conduct myself here. Visa depending, my life as a US resident could be over in a few months, or could continue for another couple of years - it’s that ambiguity that sets aside this experience from a year-abroad at Uni, for instance. It means the difference between choosing which side of the Atlantic to focus on, which is important. SF, as I’ve learned so far, is not a place you can half-heartedly coast through. And as two 11-hour flights recently reaffirmed to me - that ocean is bloody huge.
The code-uber-alles lifestyle is a lot to keep on top of - I’m not averse to working hard, but for me it was always about maintaining a healthy balance between having a creative outlet and getting work done, and at times combining the two. Here, the power that the tech industry has over all facets of life is awe-inspiring. I went to part of the LAUNCH Hackathon yesterday and everything, from the contestants, to the location, to the prizes, was immense. For those who eat, drink and breathe code, it was Mecca. I cannot imagine something of that scale ever happening anywhere in the UK. And this stuff happens all the time.
Another thing - the definition of what’s “cool” in technology is very different here to anywhere else I’ve been. Here, “cool” is taking an on-the-surface boring (but highly functional) idea, dolling it up with snazzy branding and a wall of merchandise, and throwing it out to the masses for free. This is how innovation happens, by taking real problems and approaching them with an agile, nothing-to-lose mindset and a view towards establishing and growing a community. And because these cutting-edge tools are finding their way into commercial practice (my job included), it means I no longer have to slave over a monolithic tower of Java because some archaic business license says so - which is definitely a good thing.
When it comes to my free time though, I’d much rather tinker with a toaster to try and make it generate music. Nobody would ever buy the outcome, and you wouldn’t see it on a .io domain, but it’s a bit of fun. And that’s the thing, when hobbyist coding is culturally strong-armed into joining some open-source crusade with a larger agenda, it’s a noble cause, but it smudges the line between work and recreation.
Steering back towards lifestyle, over the past few months I’ve adjusted much more to actually living here. It took a trip back to the UK to realise that the Americans do some things better, and the Brits do some things better. The weather is definitely in the camp of the former. Cadbury’s chocolate is now only in the camp of the latter, thanks to Hershey’s.
Anyway. To conclude: things are alright.
But the Superbowl sucked.


| Current |
|
| Previous |
|
| Education |
|
Background
Summary
- Third-year student at University of Science Ho Chi Minh City.
- Specialized Information System.
- Good teamwork and reporting skills.
- Have knowlegde about:
- Database: MS SQL Server, Oracle.
- Programming languages: C/C++/C#, Java.
- DataMining.
- Web: HTML, CSS, Javascript.
- Love Manga and Anime.


--- layout: default image-gallery: - image: /assets/images/projects/yingnflo-hk/Linehouse_Yingnflo_025_lo_header.jpg url: /work/yingnflo-hk - image: /assets/images/projects/gaga-coast-shanghai/Linehouse_gaga_coast_017_lo.jpg url: /work/gaga-coast-shanghai - image: /assets/images/projects/langham-hong-kong/Linehouse_Langham_011_lo.jpg url: /work/langham-hong-kong - image: /assets/images/projects/blackstar-pastry-shanghai/Linehouse_BlackStarPastry_012_LO-RES.jpg url: /work/black-star-pastry-shanghai-flagship - image: /assets/images/projects/creative-office-shanghai/Linehouse_Jianye_009_lo.jpg url: /work/creative-space-shanghai - image: /assets/images/projects/mixup-shanghai/WOsaka_Linehouse_002_LQ.jpg url: /work/mixup-shanghai - image: assets/images/main-site-images/slider/Linehouse_Bestseller_008_lo.jpg url: /work/bestseller - image: assets/images/main-site-images/slider/Linehouse_Social-Space_Slider_11.jpg url: /work/social_space - image: assets/images/main-site-images/slider/Linehouse_JNBY_Slider_8.jpg url: /work/jnby - image: assets/images/main-site-images/slider/Linehouse_BaseHall_15_LO.jpg url: /work/basehall-hong-kong - image: assets/images/main-site-images/slider/Linehouse_Teahouse_Slider_1.jpg url: /work/tingtai-tea-house - image: assets/images/main-site-images/slider/Linehouse_JohnAnthony_Slider_2.jpg url: /work/house-of-john-anthony - image: assets/images/main-site-images/slider/Linehouse_WeWork2_Slider_3.jpg url: /work/wework-weihai - image: assets/images/main-site-images/slider/Linehouse_HerschelShibuya_Slider_4.jpg url: /work/herschel-supply-tokyo - image: assets/images/main-site-images/slider/Linehouse_JohnAnthony_Slider_5.jpg url: /work/house-of-john-anthony ---


News analysis refers to the measurement of the various qualitative and quantitative attributes of textual (unstructured data) news stories. Some of these attributes are: sentiment, relevance, and novelty. Expressing news stories as numbers and metadata permits the manipulation of everyday information in a mathematical and statistical way.
This is the Analysis in which we are going to understand what type of News is Trending Most. You can do Analysis via Categories given in Menu Or Do It Manually By Clicking Here


{% include 'common/csrf_token.html' %}
${company_name}
{% if current_user.is_authenticated %}
${ current_user.username }
{% endif %}
Profile
Billing
Logout
Are you sure to leave?
×
Please click the "Logout" button below if you want to end your current session.


technical archive
Object Oriented JavaScript for beginners
As the amount of JavaScript being written in modern web applications is growing, it’s necessary to look at how we can manage our code. This post uses the example of a monkey battling game to demonstrate how we can structure JavaScript with some object oriented conventions seen in the more...The MEAN Stack on Heroku : configuring MongoDB and Gulp
I’ve been working with the MEAN stack, following an example in Jeff Dickey’s book: “Write Modern Web Apps with the MEAN Stack” to create a web application. I had it working fine locally, but encountered a few problems when deploying the app in the cloud using Heroku, which is what I'm...
MEAN stack on IBM Bluemix: configuring MongoDB
This is a follow up to a previous post: "The MEAN Stack on Heroku", which looked at deploying and building a MEAN stack application in the cloud via Heroku. This time, I'm doing the exact same thing on IBM Bluemix, but this time I will focus primarily on configuring the Bluemix...


--- layout: default title: Request a Demo --- {% include header.html has_sticky_navigation='true' exclude_main_nav='true' %} Let's meet face to face! We just received your request for a 1-on-1 demo. One of our teammates will reach out to you in the next 48 hours to get it scheduled. Go back to homepage We previously received your request for a demo. One of our teammates will reach out to you soon to get it scheduled. Go back to homepage If your organization is above 10 developers, we are happy to allocate an hour and talk about your organization's needs, demo Koding, answer all your questions. If you use your work email, your trial may be extended and you may qualify for free credit in your account. Required Invalid E-mail Required Please tell us a bit your use case, so we prioritize your request. Required REQUEST A DEMO {% include analytics.html %}


Magister Clayton:
Basilisk Plasma, honeydew, and Stakato Ichor... You have found all 3 materials. Thank you.
Now take them and go back to
Tetrarch Thifiell
. He has something to tell you.


|
QuantLib: a free/open-source library for quantitative finance
Fully annotated sources - version 1.30
|
Gamma function class. More...
#include <ql/math/distributions/gammadistribution.hpp>
Public Member Functions | |
| Real | value (Real x) const |
| Real | logValue (Real x) const |
Public Attributes | |
| QL_DEPRECATED typedef Real | argument_type |
| QL_DEPRECATED typedef Real | result_type |
Static Private Attributes | |
| static const Real | c1_ = 76.18009172947146 |
| static const Real | c2_ = -86.50532032941677 |
| static const Real | c3_ = 24.01409824083091 |
| static const Real | c4_ = -1.231739572450155 |
| static const Real | c5_ = 0.1208650973866179e-2 |
| static const Real | c6_ = -0.5395239384953e-5 |
Gamma function class.
This is a function defined by
\[ \Gamma(z) = \int_0^{\infty}t^{z-1}e^{-t}dt \]
The implementation of the algorithm was inspired by "Numerical Recipes in C", 2nd edition, Press, Teukolsky, Vetterling, Flannery, chapter 6
Definition at line 68 of file gammadistribution.hpp.
Definition at line 84 of file gammadistribution.cpp.
| QL_DEPRECATED typedef Real argument_type |
auto
or decltype
instead. Deprecated in version 1.29. Definition at line 74 of file gammadistribution.hpp.
| QL_DEPRECATED typedef Real result_type |
auto
or decltype
instead. Deprecated in version 1.29. Definition at line 80 of file gammadistribution.hpp.
|
staticprivate |
Definition at line 85 of file gammadistribution.hpp.
|
staticprivate |
Definition at line 85 of file gammadistribution.hpp.
|
staticprivate |
Definition at line 85 of file gammadistribution.hpp.
|
staticprivate |
Definition at line 85 of file gammadistribution.hpp.
|
staticprivate |
Definition at line 85 of file gammadistribution.hpp.
|
staticprivate |
Definition at line 85 of file gammadistribution.hpp.


AWA Marketing Manager named among 50 most influential CMOs in Ghana
Accra, Nov. 14, GNA - Madam Victoria Abena Takyi, Sales and Marketing Manager of Africa World Airlines (AWA), has been named among top marketing, sales and customer service professional in the list of the 50 Most Influential Chief Marketing Officers in Ghana.
The inaugural list, which is not ranked, was compiled by Avance Media, Reset Global People and Afiba Consulting and seeks to recognize the works of the individuals, who are motivating performance, broadening their market, creating new trends and brands perception and an impressive financial performance in the Ghanaian market.
A statement issued by organisers said the comprehensive list chronicles the names of the 50 best minds in the sales and marketing field in the country.
It said 21 out of the 50 professionals listed were women and the sectors cut across media, technology, banking and finance, insurance, food and beverage, telco, oil and gas, and insurance among others.
It said Madam Takyi had over a decade working experience in customer care and relations, tele-sales, direct sales, key account management, marketing and public relations in Telecommunications industry (MTN Ghana) and airline industry (AWA).
"Victoria joined AWA in 2013 as a marketing officer in charge of travel agency and corporate sales," it added.
Madam Takyi holds an MBA in International Business Management and Supply Chain Management with IIBM Institute of Management (India) and Bachelor Degree in Marketing and Computing from Regent University College of Science and Technology (Ghana).
She had her high school education at Nsaba Presbyterian Senior High School in the Central Region.
She also holds an International Air Transport Association Certificate in Dynamic Pricing and Revenue Management and Airline Leading Practices and Cost Reduction Strategies.
GNA


The examples in this section use intracommunicators.
Example
Broadcast 100 ints from process 0 to every process in the group.
MPI_Comm comm; int array[100]; int root=0; ... MPI_Bcast( array, 100, MPI_INT, root, comm);As in many of our example code fragments, we assume that some of the variables (such as comm in the above) have been assigned appropriate values.


MEET JOE BLACK A film review by Steve Rhodes Copyright 1998 Steve Rhodes RATING (0 TO ****): *** 1/2
Death, using the pseudonym of Joe Black and played with maximum charm by Brad Pitt, has come to earth to collect another soul. This time his pickup is supposed to be the extremely wealthy and wonderful man named William "Bill" Parrish, played with inviting sophistication by the great Anthony Hopkins.
In MEET JOE BLACK, director Martin Brest's remake of DEATH TAKES A HOLIDAY from 1934, the eminently likeable Death decides to take a holiday. Rather than scurrying back to heaven, he decides to stay awhile on earth and see what it's like, so he expropriates Brad Pitt's body for the purpose -- something just about everyone would like to do.
MEET JOE BLACK is an incredible movie that doesn't fit neatly into any genre. Certainly it is a wonderful romance -- think date movie of the year, but it is much more that that. At times it can be extremely poignant, at other times it is wise, and it is always interlaced with intelligent and delicious comedy.
One thing is certain, the casting is superb. Brad Pitt, showing a gift for subtle, physical comedy, delivers the best performance of his career. He deserves Oscar consideration although he will probably be ignored since the role doesn't have the maudlin seriousness that the Academy frequently prefers.
The camera loves Pitt. With his golden blonde, perfectly unkempt hair, his deep blue eyes, and his bewitching smile, he mesmerizes the audience. The distinguished Hopkins is equally handsome, albeit a few decades older.
Cast opposite these two celestial stars is Claire Forlani as Susan Parrish, Bill's younger and extremely attractive daughter. Susan is a young medical intern who falls head over heels in love with Joe -- who wouldn't?
The story starts a few days before Bill's 65th birthday. Bill's older daughter Allison (Marcia Gay Harden) is busy spending her every waking hour obsessing over her elaborate plans for Bill's big birthday party. She is married to a self-proclaimed perennial loser named Quince, played likeably by Jeffrey Tambor.
When death shows up in Pitt's body, Bill christens him Joe Black for lack of a better name. Joe makes a bargain with Bill. He will let Bill live for a while longer if he can tag along with him everywhere and try out those earthly delights he's always heard about.
Pitt plays the part as a spectacular cipher. Like an alien from outer space, Joe doesn't know how to comport himself. When introduced to Bill's family at dinner, he just stares at them and smells his food.
Eventually, in an excursion to the kitchen, Joe samples peanut butter from a jar that the butler has open. He becomes so infatuated with this, his first taste experience, that he keeps asking for peanut butter. Much later, in Susan's arms, he will finally encounter an experience that surpasses even peanut butter.

The story's main subplot has Susan's would-be husband, Drew (Jake Weber), trying to merge Bill's company with another, a move Bill comes to oppose. In a board meeting, Drew mentions that something is "as certain as death and taxes." A slightly offended and perplexed Joe muses, "Death and taxes? That's an odd pairing."
Joe, who is clueless about most human endeavors, is given some cash by Bill so he can get around town on his own. "You know about money, don't you?" Bill asks, not quite sure. "You can't buy happiness?" Joe replies with his usual big, innocent smile.
When Bill worries too much, Joe gets concerned since his time among the living is limited by Bill's health. "Easy Bill, you'll give yourself a heart attack and ruin my vacation," Joe warns.
In film that sets a new standard for tranquility, thanks to the director's methodical, dreamy pacing, its few tragedies are so striking that your heart may miss a few beats. Still, the overall feeling that will probably remain with you is a completely contented and romantic one.
Exquisitely filmed by Emmanuel Lubezki, the movie almost manages to make its three-hour length unnoticeable. In its last 45 minutes, the movie starts to end several times but keeps not quite finishing. When the ending, which does take too long in coming, arrives, it is spectacular. Like the rest of the film, it is funny, poignant, romantic and downright perfect. The last scene is even set to fireworks.
MEET JOE BLACK runs 2:57. It is rated PG-13 for sexuality, death, and a little profanity and would be fine for kids around 12 an up.
Email<EMAIL_ADDRESS>Web: www.InternetReviews.com
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


Welcome
A recurrent neural network (RNN) is a class of artificial neural network where connections between nodes form a directed graph along a temporal sequence. This allows it to exhibit temporal dynamic behavior. Unlike feedforward neural networks, RNNs can use their internal state (memory) to process sequences of inputs. This makes them applicable to tasks such as unsegmented, connected handwriting recognition[1] or speech recognition.


Create your profile
Add a channel
Create your profile
Invite your colleagues
Name your profile
Connect with Twitter
Next step


Experienced Web Developer with a demonstrated history of working in the marketing and
advertising industry.
Skilled in HTML 5, CSS 3, JQuery, Bootstrap (3,4), JavaScript
and Ruby on Rails and Basic, Angular JS,
User Interface Design. Strong engineering professional with a Bachelor’s Degree focused in
Computer Science from Progressive Educational Society Modern College of Engineering,
Shivajinagar, Pune 5.


Walter Naranjo is skilled in Sales, Public Speaking, Management, Linguistic Translation, and Leadership. A strong business development professional, and talented musician, both fused in harmony to demonstrate outstanding communication and
leadership skills. An avid motivator and influencer, and most definitely ambitious entrepreneur.


--- layout: default ---
The C++ implementation can be compiled with emscripten to produce a Javascript implementation of Jsonnet. This is how we implement our Jsonnet demo. It is not the fastest possible Javascript implementation of Jsonnet, but it is easy to build, and is sufficient for many purposes.
To compile it, first install emscripten and ensure em++ and emcc are in your path. Then make libjsonnet.js.
An unofficial nodejs package of Jsonnet is also available.


Madonna Cancels ‘Madame X’ Shows Due To ‘Overwhelming Pain’
November 29, 2019
Madonna Cancels ‘Madame X’ Shows Due To ‘Overwhelming Pain’
Madonna is currently performing in her “Madame X” world tour across North America and has had to cancel a number of shows. According to the BBC, the “Like a Prayer” hitmaker was experiencing overwhelming pain.
“Please forgive this unexpected turn of events,” she wrote on Facebook.
“Doing my show every night brings me so much joy and to cancel is a kind of punishment… But the pain I’m in right now is overwhelming, and I must rest and follow doctor’s orders,” the “Material Girl” songstress continued.
Madonna didn’t specify what injury she had been suffering but told fans earlier this month in San Francisco she had a “torn ligament” and “a bad knee.”
As previously reported by The Inquisitr, the “Don’t Cry For Me Argentina” chart-topper had already postponed her opening shows in New York at the BAM Howard Gilman Opera House because of “highly specialized production elements.” Shortly after, she had to cancel one of the shows due to injury.
Ahead of her Los Angeles stint of shows, she called off the opening night.
Live Nation, who are the tour promoters, has stated the reason for not rescheduling is due to tight scheduling through the balance of the year. Those who purchased tickets will be issued a refund.
The European leg she is scheduled to embark on next year has already started to come with a few complications.
According to Stereoboard, her first London, U.K. show on January 26 at the Palladium has been rescheduled to February 16, becoming the last show of the residency. The 15-show run at the venue will now begin on January 17.
She will kick off the European leg in Lisbon, Portugal, and will also visit Paris, France, after London.
Despite the constant cancelations and postponing, Madonna’s “Madame X” show went down will fans who attended the opening night in New York City.
“Amazing opening night of #Madonna ‘Madame X’ Tour! Concert,” one user wrote on social media.
“Ok so the ‘Madame X’ Tour was the best thing I’ve ever seen. Possibly Madonna’s artistic pinnacle. Incredible,” another shared.
Loading...
The setlist consists of a lot of songs taken from Madame X, her latest release, as well as many of her hit singles including “Express Yourself,” “La Isla Bonita,” and “Papa Don’t Preach,” to name a few.
Ahead of the tour, she revealed that no phones or cameras would be permitted when entering the venues.
Over the past few months, Madonna has been sharing her own professional photography and videos from the tour to her Instagram page.


void STP_SetMstConfigRevisionLevel (STP_BRIDGE* bridge, unsigned short revisionLevel);
Sets the Revision Level field of the MST Configuration Identifier.
This function may be called only for MSTP bridges (i.e., if STP_VERSION_MSTP
was passed
to STP_CreateBridge or
STP_SetStpVersion).
See §13.8 in 802.1Q-2018 for more information about the MST Configuration Identifier.
If this function is not called, a default revision level is used which is generated by STP_CreateBridge. See the Remarks section there for more information.
This function may not be called from within an STP callback.


|
SimpleITK
2.0.0
|
This filter computes the distance map of the input image as an approximation with pixel accuracy to the Euclidean distance. More...
#include <sitkDanielssonDistanceMapImageFilter.h>
Public Types | |
| using | PixelIDTypeList = IntegerPixelIDTypeList |
| using | Self = DanielssonDistanceMapImageFilter |
| Public Types inherited from itk::simple::ImageFilter | |
| using | Self = ImageFilter |
| Public Types inherited from itk::simple::ProcessObject | |
| using | Self = ProcessObject |
Public Member Functions | |
| DanielssonDistanceMapImageFilter () | |
| Image | Execute (const Image &image1) |
| bool | GetInputIsBinary () const |
| std::string | GetName () const |
| bool | GetSquaredDistance () const |
| bool | GetUseImageSpacing () const |
| Self & | InputIsBinaryOff () |
| Self & | InputIsBinaryOn () |
| Self & | SetInputIsBinary (bool InputIsBinary) |
| Self & | SetSquaredDistance (bool SquaredDistance) |
| Self & | SetUseImageSpacing (bool UseImageSpacing) |
| Self & | SquaredDistanceOff () |
| Self & | SquaredDistanceOn () |
| std::string | ToString () const |
| Self & | UseImageSpacingOff () |
| Self & | UseImageSpacingOn () |
| virtual | ~DanielssonDistanceMapImageFilter () |
| Public Member Functions inherited from itk::simple::ImageFilter | |
| ImageFilter () | |
| virtual | ~ImageFilter ()=0 |
| Public Member Functions inherited from itk::simple::ProcessObject | |
| virtual void | Abort () |
| virtual int | AddCommand (itk::simple::EventEnum event, const std::function< void()> &func) |
| Directly add a callback to observe an event. More... | |
| virtual int | AddCommand (itk::simple::EventEnum event, itk::simple::Command &cmd) |
| Add a Command Object to observer the event. More... | |
| virtual float | GetProgress () const |
| An Active Measurement of the progress of execution. More... | |
| virtual bool | HasCommand (itk::simple::EventEnum event) const |
| Query of this object has any registered commands for event. More... | |
| ProcessObject () | |
| virtual void | RemoveAllCommands () |
| Remove all registered commands. More... | |
| virtual | ~ProcessObject () |
| virtual void | DebugOn () |
| virtual void | DebugOff () |
| virtual bool | GetDebug () const |
| virtual void | SetDebug (bool debugFlag) |
| virtual void | SetNumberOfThreads (unsigned int n) |
| virtual unsigned int | GetNumberOfThreads () const |
| virtual void | SetNumberOfWorkUnits (unsigned int n) |
| virtual unsigned int | GetNumberOfWorkUnits () const |
Private Types | |
| using | MemberFunctionType = Image(Self::*)(const Image &image1) |
Private Member Functions | |
| template<class TImageType > | |
| Image | ExecuteInternal (const Image &image1) |
Private Attributes | |
| bool | m_InputIsBinary {false} |
| std::unique_ptr< detail::MemberFunctionFactory< MemberFunctionType > > | m_MemberFactory |
| bool | m_SquaredDistance {false} |
| bool | m_UseImageSpacing {false} |
Friends | |
| struct | detail::MemberFunctionAddressor< MemberFunctionType > |
Additional Inherited Members | |
| Static Public Member Functions inherited from itk::simple::ProcessObject | |
| static bool | GetGlobalDefaultDebug () |
| static void | GlobalDefaultDebugOff () |
| static void | GlobalDefaultDebugOn () |
| static void | SetGlobalDefaultDebug (bool debugFlag) |
| static void | GlobalWarningDisplayOn () |
| static void | GlobalWarningDisplayOff () |
| static void | SetGlobalWarningDisplay (bool flag) |
| static bool | GetGlobalWarningDisplay () |
| static double | GetGlobalDefaultCoordinateTolerance () |
| Access the global tolerance to determine congruent spaces. More... | |
| static void | SetGlobalDefaultCoordinateTolerance (double) |
| Access the global tolerance to determine congruent spaces. More... | |
| static double | GetGlobalDefaultDirectionTolerance () |
| Access the global tolerance to determine congruent spaces. More... | |
| static void | SetGlobalDefaultDirectionTolerance (double) |
| Access the global tolerance to determine congruent spaces. More... | |
| static bool | SetGlobalDefaultThreader (const std::string &threader) |
| Set/Get the default threader used for process objects. More... | |
| static std::string | GetGlobalDefaultThreader () |
| Set/Get the default threader used for process objects. More... | |
| static void | SetGlobalDefaultNumberOfThreads (unsigned int n) |
| static unsigned int | GetGlobalDefaultNumberOfThreads () |
| Set/Get the default threader used for process objects. More... | |
| Protected Member Functions inherited from itk::simple::ImageFilter | |
| void | CheckImageMatchingDimension (const Image &image1, const Image &image2, const std::string &image2Name) |
| void | CheckImageMatchingPixelType (const Image &image1, const Image &image2, const std::string &image2Name) |
| void | CheckImageMatchingSize (const Image &image1, const Image &image2, const std::string &image2Name) |
| Protected Member Functions inherited from itk::simple::ProcessObject | |
| virtual unsigned long | AddITKObserver (const itk::EventObject &, itk::Command *) |
| virtual itk::ProcessObject * | GetActiveProcess () |
| virtual void | OnActiveProcessDelete () |
| virtual void | onCommandDelete (const itk::simple::Command *cmd) noexcept |
| virtual void | PreUpdate (itk::ProcessObject *p) |
| virtual void | RemoveITKObserver (EventCommand &e) |
| Protected Member Functions inherited from itk::simple::NonCopyable | |
| NonCopyable ()=default | |
| NonCopyable (const NonCopyable &)=delete | |
| NonCopyable & | operator= (const NonCopyable &)=delete |
| Static Protected Member Functions inherited from itk::simple::ImageFilter | |
| template<class TImageType > | |
| static void | FixNonZeroIndex (TImageType *img) |
| Static Protected Member Functions inherited from itk::simple::ProcessObject | |
| template<class TImageType > | |
| static TImageType::ConstPointer | CastImageToITK (const Image &img) |
| template<class TPixelType , unsigned int VImageDimension, unsigned int VLength, template< typename, unsigned int > class TVector> | |
| static Image | CastITKToImage (itk::Image< TVector< TPixelType, VLength >, VImageDimension > *img) |
| template<class TImageType > | |
| static Image | CastITKToImage (TImageType *img) |
| static const itk::EventObject & | GetITKEventObject (EventEnum e) |
| template<typename T > | |
| static std::ostream & | ToStringHelper (std::ostream &os, const T &v) |
| static std::ostream & | ToStringHelper (std::ostream &os, const char &v) |
| static std::ostream & | ToStringHelper (std::ostream &os, const signed char &v) |
| static std::ostream & | ToStringHelper (std::ostream &os, const unsigned char &v) |
This filter computes the distance map of the input image as an approximation with pixel accuracy to the Euclidean distance.
TInputImage
Input Image Type
TOutputImage
Output Image Type
TVoronoiImage
Voronoi Image Type. Note the default value is TInputImage.
The input is assumed to contain numeric codes defining objects. The filter will produce as output the following images:
This filter is N-dimensional and known to be efficient in computational time. The algorithm is the N-dimensional version of the 4SED algorithm given for two dimensions in:
Danielsson, Per-Erik. Euclidean Distance Mapping. Computer Graphics and Image Processing 14, 227-248 (1980).
Definition at line 78 of file sitkDanielssonDistanceMapImageFilter.h.
|
private |
Setup for member function dispatching
Definition at line 150 of file sitkDanielssonDistanceMapImageFilter.h.
Define the pixels types supported by this filter
Definition at line 90 of file sitkDanielssonDistanceMapImageFilter.h.
Definition at line 80 of file sitkDanielssonDistanceMapImageFilter.h.
|
virtual |
Destructor
| itk::simple::DanielssonDistanceMapImageFilter::DanielssonDistanceMapImageFilter | ( | ) |
Default Constructor that takes no arguments and initializes default parameters
Execute the filter on the input image
|
private |
|
inline |
Get if the input is binary. See SetInputIsBinary() .
Definition at line 105 of file sitkDanielssonDistanceMapImageFilter.h.
|
inlinevirtual |
Name of this class
Implements itk::simple::ProcessObject.
Definition at line 136 of file sitkDanielssonDistanceMapImageFilter.h.
|
inline |
Get the distance squared.
Definition at line 119 of file sitkDanielssonDistanceMapImageFilter.h.
|
inline |
Get whether spacing is used.
Definition at line 133 of file sitkDanielssonDistanceMapImageFilter.h.
|
inline |
Definition at line 100 of file sitkDanielssonDistanceMapImageFilter.h.
|
inline |
Set the value of InputIsBinary to true or false respectfully.
Definition at line 99 of file sitkDanielssonDistanceMapImageFilter.h.
|
inline |
Set if the input is binary. If this variable is set, each nonzero pixel in the input image will be given a unique numeric code to be used by the Voronoi partition. If the image is binary but you are not interested in the Voronoi regions of the different nonzero pixels, then you need not set this.
Definition at line 96 of file sitkDanielssonDistanceMapImageFilter.h.
|
inline |
Set if the distance should be squared.
Definition at line 110 of file sitkDanielssonDistanceMapImageFilter.h.
|
inline |
Set if image spacing should be used in computing distances.
Definition at line 124 of file sitkDanielssonDistanceMapImageFilter.h.
|
inline |
Definition at line 114 of file sitkDanielssonDistanceMapImageFilter.h.
|
inline |
Set the value of SquaredDistance to true or false respectfully.
Definition at line 113 of file sitkDanielssonDistanceMapImageFilter.h.
|
virtual |
Print ourselves out
Reimplemented from itk::simple::ProcessObject.
|
inline |
Definition at line 128 of file sitkDanielssonDistanceMapImageFilter.h.
|
inline |
Set the value of UseImageSpacing to true or false respectfully.
Definition at line 127 of file sitkDanielssonDistanceMapImageFilter.h.
|
friend |
Definition at line 154 of file sitkDanielssonDistanceMapImageFilter.h.
|
private |
Definition at line 159 of file sitkDanielssonDistanceMapImageFilter.h.
|
private |
Definition at line 156 of file sitkDanielssonDistanceMapImageFilter.h.
|
private |
Definition at line 161 of file sitkDanielssonDistanceMapImageFilter.h.
|
private |
Definition at line 163 of file sitkDanielssonDistanceMapImageFilter.h.


Numbers 21:15
And at the stream of the brooks that goeth down to the dwelling of Ar, and lieth upon the border of Moab.


--- layout: post title: "Chain of Responsibility as catamorphisms" description: "The Chain of Responsibility design pattern can be viewed as a list fold over the First monoid, followed by a Maybe fold." date: 2019-07-22 14:11 UTC tags: [Software Design, Design Patterns] image: "/content/binary/chain-of-responsibility-as-a-linked-list-double-fold.png" image_alt: "Chain of Responsibility illustrated as a linked list, with two consecutive folds." --- {% include JB/setup %}
{{ page.description }}
This article is part of a series of articles about specific design patterns and their category theory counterparts. In it, you'll see how the Chain of Responsibility design pattern is equivalent to a succession of catamorphisms. First, you apply the First Maybe monoid over the list catamorphism, and then you conclude the reduction with the Maybe catamorphism.
The Chain of Responsibility design pattern gives you a way to model cascading conditionals with an object structure. It's a chain (or linked list) of objects that all implement the same interface (or base class). Each object (apart from the the last) has a reference to the next object in the list.
A client (some other code) calls a method on the first object in the list. If that object can handle the request, it does so, and the interaction ends there. If the method returns a value, the object returns the value.
If the first object determines that it can't handle the method call, it calls the next object in the chain. It only knows the next object as the interface, so the only way it can delegate the call is by calling the same method as the first one. In the above diagram, Imp1 can't handle the method call, so it calls the same method on Imp2, which also can't handle the request and delegates responsibility to Imp3. In the diagram, Imp3 can handle the method call, so it does so and returns a result that propagates back up the chain. In that particular example, Imp4 never gets involved.
You'll see an example below.
One of the advantages of the pattern is that you can rearrange the chain to change its behaviour. You can even do this at run time, if you'd like, since all objects implement the same interface.
Consider an online system that maintains user profiles for users. A user is modelled with the User
class:
public User(int id, string name, string email, bool useGravatar, bool useIdenticon)
While I only show the signature of the class' constructor, it should be enough to give you an idea. If you need more details, the entire example code base is available on GitHub.
Apart from an id
, a name
and email
address, a user also has two flags. One flag tracks whether the user wishes to use his or her Gravatar, while another flag tracks if the user would like to use an Identicon. Obviously, both flags could be true
, in which case the current business rule states that the Gravatar should take precedence.
If none of the flags are set, users might still have a picture associated with their profile. This could be a picture that they've uploaded to the system, and is being tracked by a database.
If no user icon can be found or generated, ultimately the system should use a fallback, default icon:
To summarise, the current rules are:
To request an icon, a client can use the IIconReader
interface:
public interface IIconReader { Icon ReadIcon(User user); }
The Icon
class is just a Value Object wrapper around a URL. The idea is that such a URL can be used in an img
tag to show the icon. Again, the full source code is available on GitHub if you'd like to investigate the details.
The various rules for icon retrieval can be implemented using this interface.
Although you don't have to implement the classes in the order in which you are going to compose them, it seems natural to do so, starting with the Gravatar implementation.
public class GravatarReader : IIconReader { private readonly IIconReader next; public GravatarReader(IIconReader next) { this.next = next; } public Icon ReadIcon(User user) { if (user.UseGravatar) return new Icon(new Gravatar(user.Email).Url); return next.ReadIcon(user); } }
The GravatarReader
class both implements the IIconReader
interface, but also decorates another object of the same polymorphic type. If user.UseGravatar
is true
, it generates the appropriate Gravatar URL based on the user's Email
address; otherwise, it delegates the work to the next
object in the Chain of Responsibility.
The Gravatar
class contains the implementation details to generate the Gravatar Url
. Again, please refer to the GitHub repository if you're interested in the details.
When you compose the chain, according to the above business logic, the next type of icon you should attempt to generate is an Identicon. It's natural to implement the Identicon reader next, then:
public class IdenticonReader : IIconReader { private readonly IIconReader next; public IdenticonReader(IIconReader next) { this.next = next; } public Icon ReadIcon(User user) { if (user.UseIdenticon) return new Icon(new Uri(baseUrl, HashUser(user))); return next.ReadIcon(user); } // Implementation details go here... }
Again, I'm omitting implementation details in order to focus on the Chain of Responsibility design pattern. If user.UseIdenticon
is true
, the IdenticonReader
generates the appropriate Identicon and returns the URL for it; otherwise, it delegates the work to the next
object in the chain.
The DBIconReader
class attempts to find an icon ID in a database. If it succeeds, it creates a URL corresponding to that ID. The assumption is that that resource exists; either it's a file on disk, or it's an image resource generated on the spot based on binary data stored in the database.
public class DBIconReader : IIconReader { private readonly IUserRepository repository; private readonly IIconReader next; public DBIconReader(IUserRepository repository, IIconReader next) { this.repository = repository; this.next = next; } public Icon ReadIcon(User user) { if (!repository.TryReadIconId(user.Id, out string iconId)) return next.ReadIcon(user); var parameters = new Dictionary<string, string> { { "iconId", iconId } }; return new Icon(urlTemplate.BindByName(baseUrl, parameters)); } private readonly Uri baseUrl = new Uri("https://example.com"); private readonly UriTemplate urlTemplate = new UriTemplate("users/{iconId}/icon"); }
This class demonstrates some variations in the way you can implement the Chain of Responsibility design pattern. The above GravatarReader
and IdenticonReader
classes both follow the same implementation pattern of checking a condition, and then performing work if the condition is true
. The delegation to the next object in the chain happens, in those two classes, outside of the if
statement.
The DBIconReader
class, on the other hand, reverses the structure of the code. It uses a Guard Clause to detect whether to exit early, which is done by delegating work to the next
object in the chain.
If TryReadIconId
returns true
, however, the ReadIcon
method proceeds to create the appropriate icon URL.
Another variation on the Chain of Responsibility design pattern demonstrated by the DBIconReader
class is that it takes a second dependency, apart from next
. The repository
is the usual misapplication of the Repository design pattern that everyone think they use correctly. Here, it's used in the common sense to provide access to a database. The main point, though, is that you can add as many other dependencies to a link in the chain as you'd like. All links, apart from the last, however, must have a reference to the next
link in the chain.
Like linked lists, a Chain of Responsibility has to ultimately terminate. You can use the following DefaultIconReader
for that.
public class DefaultIconReader : IIconReader { public Icon ReadIcon(User user) { return Icon.Default; } }
This class unconditionally returns the Default
icon. Notice that it doesn't have any next
object it delegates to. This terminates the chain. If no previous implementation of the IIconReader
has returned an Icon
for the user
, this one does.
With four implementations of IIconReader
, you can now compose the Chain of Responsibility:
IIconReader reader = new GravatarReader( new IdenticonReader( new DBIconReader(repo, new DefaultIconReader())));
The first link in the chain is a GravatarReader
object that contains an IdenticonReader
object as its next
link, and so on. Referring back to the source code of GravatarReader
, notice that its next
dependency is declared as an IIconReader
. Since the IdenticonReader
class implements that interface, you can compose the chain like this, but if you later decide to change the order of the objects, you can do so simply by changing the composition. You could remove objects altogether, or add new classes, and you could even do this at run time, if required.
The DBIconReader
class requires an extra IUserRepository
dependency, here simply an existing object called repo
.
The DefaultIconReader
takes no other dependencies, so this effectively terminates the chain. If you try to pass another IIconReader
to its constructor, the code doesn't compile.
When evaluating whether a design is a functional architecture, I often port the relevant parts to Haskell. You can do the same with the above example, and put it in a form where it's clearer that the Chain of Responsibility pattern is equivalent to two well-known catamorphisms.
Readers not comfortable with Haskell can skip the next few sections. The object-oriented example continues below.
User
and Icon
types are defined by types equivalent to above. There's no explicit interface, however. Creation of Gravatars and Identicons are both pure functions with the type User -> Maybe Icon
. Here's the Gravatar function, but the Identicon function looks similar:
gravatarUrl :: String -> String gravatarUrl email = "https://www.gravatar.com/avatar/" ++ show (hashString email :: MD5Digest) getGravatar :: User -> Maybe Icon getGravatar u = if useGravatar u then Just $ Icon $ gravatarUrl $ userEmail u else Nothing
Reading an icon ID from a database, however, is an impure operation, so the function to do this has the type User -> IO (Maybe Icon)
.
Notice that the database icon-querying function has the return type IO (Maybe Icon)
. In the introduction you read that the Chain of Responsibility design pattern is a sequence of catamorphisms - the first one over a list of First
values. While First
is, in itself, a Semigroup
instance, it gives rise to a Monoid
instance when combined with Maybe
. Thus, to showcase the abstractions being used, you could create a list of Maybe (First Icon)
values. This forms a Monoid
, so is easy to fold.
The problem with that, however, is that IO
is strict under evaluation, so while it works, it's no longer lazy. You can combine IO (Maybe (First Icon))
values, but it leads to too much I/O activity.
You can solve this problem with a newtype wrapper:
newtype FirstIO a = FirstIO (MaybeT IO a) deriving (Functor, Applicative, Monad, Alternative) firstIO :: IO (Maybe a) -> FirstIO a firstIO = FirstIO . MaybeT getFirstIO :: FirstIO a -> IO (Maybe a) getFirstIO (FirstIO (MaybeT x)) = x instance Semigroup (FirstIO a) where (<>) = (<|>) instance Monoid (FirstIO a) where mempty = empty
This uses the GeneralizedNewtypeDeriving
GHC extension to automatically make FirstIO
Functor
, Applicative
, Monad
, and Alternative
. It also uses the Alternative
instance to implement Semigroup
and Monoid
. You may recall from the documentation that Alternative
is already a "monoid on applicative functors."
You now have three functions with different types: two pure functions with the type User -> Maybe Icon
and one impure database-bound function with the type User -> IO (Maybe Icon)
. In order to have a common abstraction, you should align them so that all types match. At first glance, User -> IO (Maybe (First Icon))
seems like a type that fits all implementations, but that causes too much I/O to take place, so instead, use User -> FirstIO Icon
. Here's how to lift the pure getGravatar
function:
getGravatarIO :: User -> FirstIO Icon getGravatarIO = firstIO . return . getGravatar
You can lift the other functions in similar fashion, to produce getGravatarIO
, getIdenticonIO
, and getDBIconIO
, all with the mutual type User -> FirstIO Icon
.
The goal of the Haskell proof of concept is to compose a function that can provide an Icon
for any User
- just like the above C# composition that uses Chain of Responsibility. There's, however, no way around impurity, because one of the steps involve a database, so the aim is a composition with the type User -> IO Icon
.
While a more compact composition is possible, I'll show it in a way that makes the catamorphisms explicit:
getIcon :: User -> IO Icon getIcon u = do let lazyIcons = fmap (\f -> f u) [getGravatarIO, getIdenticonIO, getDBIconIO] m <- getFirstIO $ fold lazyIcons return $ fromMaybe defaultIcon m
The getIcon
function starts with a list of all three functions. For each of them, it calls the function with the User
value u
. This may seem inefficient and redundant, because all three function calls may not be required, but since the return values are FirstIO
values, all three function calls are lazily evaluated - even under IO
. The result, lazyIcons
, is a [FirstIO Icon]
value; i.e. a lazily evaluated list of lazily evaluated values.
This first step is just to put the potential values in a form that's recognisable. You can now fold
the lazyIcons
to a single FirstIO Icon
value, and then use getFirstIO
to unwrap it. Due to do
notation, m
is a Maybe Icon
value.
This is the first catamorphism. Granted, the generalisation that fold
offers is not really required, since lazyIcons
is a list; mconcat
would have worked just as well. I did, however, choose to use fold
(from Data.Foldable
) to emphasise the point. While the fold
function itself isn't the catamorphism for lists, we know that it's derived from the list catamorphism.
The final step is to utilise the Maybe catamorphism to reduce the Maybe Icon
value to an Icon
value. Again, the getIcon
function doesn't use the Maybe catamorphism directly, but rather the derived fromMaybe
function. The Maybe catamorphism is the maybe
function, but you can trivially implement fromMaybe
with maybe
.
For golfers, it's certainly possible to write this function in a more compact manner. Here's a point-free version:
getIcon :: User -> IO Icon getIcon = fmap (fromMaybe defaultIcon) . getFirstIO . fold [getGravatarIO, getIdenticonIO, getDBIconIO]
This alternative version utilises that a -> m
is a Monoid
instance when m
is a Monoid
instance. That's the reason that you can fold
a list of functions. The more explicit version above doesn't do that, but the behaviour is the same in both cases.
That's all the Haskell code we need to discern the universal abstractions involved in the Chain of Responsibility design pattern. We can now return to the C# code example.
The Chain of Responsibility design pattern is often illustrated like above, in a staircase-like diagram. There's, however, no inherent requirement to do so. You could also flatten the diagram:
This looks a lot like a linked list.
The difference is, however, that the terminator of a linked list is usually empty. Here, however, you have two types of objects. All objects apart from the rightmost object represent a potential. Each object may, or may not, handle the method call and produce an outcome; if an object can't handle the method call, it'll delegate to the next object in the chain.
The rightmost object, however, is different. This object can't delegate any further, but must handle the method call. In the icon reader example, this is the DefaultIconReader
class.
Once you start to see most of the list as a list of potential values, you may realise that you'll be able to collapse into it a single potential value. This is possible because a list of values where you pick the first non-empty value forms a monoid. This is sometimes called the First monoid.
In other words, you can reduce, or fold, all of the list, except the rightmost value, to a single potential value:
When you do that, however, you're left with a single potential value. The result of folding most of the list is that you get the leftmost non-empty value in the list. There's no guarantee, however, that that value is non-empty. If all the values in the list are empty, the result is also empty. This means that you somehow need to combine a potential value with a value that's guaranteed to be present: the terminator.
You can do that wither another fold:
This second fold isn't a list fold, but rather a Maybe fold.
The First monoid is a monoid over Maybe, so add a Maybe
class to the code base. In Haskell, the catamorphism for Maybe is called maybe
, but that's not a good method name in object-oriented design. Another option is some variation of fold, but in C#, this functionality tends to be called Aggregate
, at least for IEnumerable<T>
, so I'll reuse that terminology:
public TResult Aggregate<TResult>(TResult @default, Func<T, TResult> func) { if (func == null) throw new ArgumentNullException(nameof(func)); return hasItem ? func(item) : @default; }
You can implement another, more list-like Aggregate
overload from this one, but for this article, you don't need it.
In the above code examples, DBIconReader
depends on IUserRepository
, which defined this method:
bool TryReadIconId(int userId, out string iconId);
From Tester-Doer isomorphisms we know, however, that such a design is isomorphic to returning a Maybe value, and since that's more composable, do that:
Maybe<string> ReadIconId(int userId);
This requires you to refactor the DBIconReader
implementation of the ReadIcon
method:
public Icon ReadIcon(User user) { Maybe<string> mid = repository.ReadIconId(user.Id); Lazy<Icon> lazyResult = mid.Aggregate( @default: new Lazy<Icon>(() => next.ReadIcon(user)), func: id => new Lazy<Icon>(() => CreateIcon(id))); return lazyResult.Value; }
A few things are worth a mention. Notice that the above Aggregate
method (the Maybe catamorphism) requires you to supply a @default
value (to be used if the Maybe object is empty). In the Chain of Responsibility design pattern, however, the fallback value is produced by calling the next
object in the chain. If you do this unconditionally, however, you perform too much work. You're only supposed to call next
if the current object can't handle the method call.
The solution is to aggregate the mid
object to a Lazy<Icon>
and then return its Value
. The @default
value is now a lazy computation that calls next
only if its Value
is read. When mid
is populated, on the other hand, the lazy computation calls the private CreateIcon
method when Value
is accessed. The private CreateIcon
method contains the same logic as before the refactoring.
This change of DBIconReader
isn't strictly necessary in order to change the overall Chain of Responsibility to a pair of catamorphisms, but serves, I think, as a nice introduction to the use of the Maybe catamorphism.
Previously, the IIconReader
interface required each implementation to return an Icon
object:
public interface IIconReader { Icon ReadIcon(User user); }
When you have an object like GravatarReader
that may or may not return an Icon
, this requirement leads toward the Chain of Responsibility design pattern. You can, however, shift the responsibility of what to do next by changing the interface:
public interface IIconReader { Maybe<Icon> ReadIcon(User user); }
An implementation like GravatarReader
becomes simpler:
public class GravatarReader : IIconReader { public Maybe<Icon> ReadIcon(User user) { if (user.UseGravatar) return new Maybe<Icon>(new Icon(new Gravatar(user.Email).Url)); return new Maybe<Icon>(); } }
No longer do you have to pass in a next
dependency. Instead, you just return an empty Maybe<Icon>
if you can't handle the method call. The same change applies to the IdenticonReader
class.
Since Maybe is a functor, and the DBIconReader
already works on a Maybe<string>
value, its implementation is greatly simplified:
public Maybe<Icon> ReadIcon(User user) { return repository.ReadIconId(user.Id).Select(CreateIcon); }
Since ReadIconId
returns a Maybe<string>
, you can simply use Select
to transform the icon ID to an Icon
object if the Maybe is populated.
As an intermediate step, you can compose the various readers using a Coalescing Composite:
public class CompositeIconReader : IIconReader { private readonly IIconReader[] iconReaders; public CompositeIconReader(params IIconReader[] iconReaders) { this.iconReaders = iconReaders; } public Maybe<Icon> ReadIcon(User user) { foreach (var iconReader in iconReaders) { var mIcon = iconReader.ReadIcon(user); if (IsPopulated(mIcon)) return mIcon; } return new Maybe<Icon>(); } private static bool IsPopulated<T>(Maybe<T> m) { return m.Aggregate(false, _ => true); } }
I prefer a more explicit design over this one, so this is just an intermediate step. This IIconReader
implementation composes an array of other IIconReader
objects and queries each in order to return the first populated Maybe value it finds. If it doesn't find any populated value, it returns an empty Maybe object.
You can now compose your IIconReader
objects into a Composite:
IIconReader reader = new CompositeIconReader( new GravatarReader(), new IdenticonReader(), new DBIconReader(repo));
While this gives you a single object on which you can call ReadIcon
, the return value of that method is still a Maybe<Icon>
object. You still need to reduce the Maybe<Icon>
object to an Icon
object. You can do this with a Maybe helper method:
public T GetValueOrDefault(T @default) { return Aggregate(@default, x => x); }
Given a User
object named user
, you can now use the composition and the GetValueOrDefault
method to get an Icon
object:
Icon icon = reader.ReadIcon(user).GetValueOrDefault(Icon.Default);
First you use the composed reader
to produce a Maybe<Icon>
object, and then you use the GetValueOrDefault
method to reduce the Maybe<Icon>
object to an Icon
object.
The latter of these two steps, GetValueOrDefault
, is already based on the Maybe catamorphism, but the first step is still too implicit to clearly show the nature of what's actually going on. The next step is to refactor the Coalescing Composite to a list of monoidal values.
While not strictly necessary, you can introduce a First<T>
wrapper:
public sealed class First<T> { public First(T item) { if (item == null) throw new ArgumentNullException(nameof(item)); Item = item; } public T Item { get; } public override bool Equals(object obj) { if (!(obj is First<T> other)) return false; return Equals(Item, other.Item); } public override int GetHashCode() { return Item.GetHashCode(); } }
In this particular example, the First<T>
class adds no new capabilities, so it's technically redundant. You could add to it methods to combine two First<T>
objects into one (since First forms a semigroup), and perhaps a method or two to accumulate multiple values, but in this article, none of those are required.
While the class as shown above doesn't add any behaviour, I like that it signals intent, so I'll use it in that role.
Like in the above Haskell code, you'll need to be able to combine two First<T>
objects in a lazy fashion, in such a way that if the first object is populated, the I/O associated with producing the second value never happens. In Haskell I addressed that concern with a newtype
that, among other abstractions, is a monoid. You can do the same in C# with an extension method:
public static Lazy<Maybe<First<T>>> FindFirst<T>( this Lazy<Maybe<First<T>>> m, Lazy<Maybe<First<T>>> other) { if (m.Value.IsPopulated()) return m; return other; } private static bool IsPopulated<T>(this Maybe<T> m) { return m.Aggregate(false, _ => true); }
The FindFirst
method returns the first (leftmost) non-empty object of two options. It's a lazy version of the First monoid, and that's still a monoid. It's truly lazy because it never accesses the Value
property on other
. While it has to force evaluation of the first lazy computation, m
, it doesn't have to evaluate other
. Thus, whenever m
is populated, other
can remain non-evaluated.
Since monoids accumulate, you can also write an extension method to implement that functionality:
public static Lazy<Maybe<First<T>>> FindFirst<T>(this IEnumerable<Lazy<Maybe<First<T>>>> source) { var identity = new Lazy<Maybe<First<T>>>(() => new Maybe<First<T>>()); return source.Aggregate(identity, (acc, x) => acc.FindFirst(x)); }
This overload just uses the earlier FindFirst
extension method to fold an arbitrary number of lazy First<T>
objects into one. Notice that Aggregate
is the C# name for the list catamorphisms.
You can now compose the desired functionality using the basic building blocks of monoids, functors, and catamorphisms.
The goal is still a function that takes a User
object as input and produces an Icon
object as output. While you could compose that functionality directly in-line where you need it, I think it may be helpful to package the composition in a Facade object.
public class IconReaderFacade { private readonly IReadOnlyCollection<IIconReader> readers; public IconReaderFacade(IUserRepository repository) { readers = new IIconReader[] { new GravatarReader(), new IdenticonReader(), new DBIconReader(repository) }; } public Icon ReadIcon(User user) { IEnumerable<Lazy<Maybe<First<Icon>>>> lazyIcons = readers .Select(r => new Lazy<Maybe<First<Icon>>>(() => r.ReadIcon(user).Select(i => new First<Icon>(i)))); Lazy<Maybe<First<Icon>>> m = lazyIcons.FindFirst(); return m.Value.Aggregate(Icon.Default, fi => fi.Item); } }
When you initialise an IconReaderFacade
object, it creates an array of the desired readers
. Whenever ReadIcon
is invoked, it first transforms all those readers to a sequence of potential icons. All the values in the sequence are lazily evaluated, so in this step, nothing actually happens, even though it looks as though all readers' ReadIcon
method gets called. The Select
method is a structure-preserving map, so all readers are still potential producers of Icon
objects.
You now have an IEnumerable<Lazy<Maybe<First<Icon>>>>
, which must be a good candidate for the prize for the most nested generic .NET type of 2019. It fits, though, the input type for the above FindFirst
overload, so you can call that. The result is a single potential value m
. That's the list catamorphism applied.
Finally, you force evaluation of the lazy computation and apply the Maybe catamorphism (Aggregate
). The @default
value is Icon.Default
, which gets returned if m
turns out to be empty. When m
is populated, you pull the Item
out of the First
object. In either case, you now have an Icon
object to return.
This composition has exactly the same behaviour as the initial Chain of Responsibility implementation, but is now composed from universal abstractions.
The Chain of Responsibility design pattern describes a flexible way to implement conditional logic. Instead of relying on keywords like if
or switch
, you can compose the conditional logic from polymorphic objects. This gives you several advantages. One is that you get better separations of concerns, which will tend to make it easier to refactor the code. Another is that it's possible to change the behaviour at run time, by moving the objects around.
You can achieve a similar design, with equivalent advantages, by composing polymorphically similar functions in a list, map the functions to a list of potential values, and then use the list catamorphism to reduce many potential values to one. Finally, you apply the Maybe catamorphism to produce a value, even if the potential value is empty.


Justin's Pizza
If its pizza you want, We got it for the LOW LOW!
if no option chosen small will be ordered
Small
Medium
Large
If no option chosen cheese will be ordered
Cheese
Anchovies
Pepperoni
Order!
Orders:
Order Detail:
Pizza Size:
Pizza Toppings:
Order Total:
$
.00