Ste*_*orn 6 haskell functional-programming
我刚读完“学到Haskell来造福伟大!”。书,所以我的问题可能很幼稚。我不明白的是如何从纯代码中调用“不纯”的IO函数。
这是一个用C#编写的工作示例。在我们的业务逻辑中,我们根据天气计划一些行动。我们以通常的C#方式进行操作。
interface IWeatherForecast
{
WeatherData GetWeather(Location location, DateTime timestamp);
}
// concrete implementation reading weather from DB
class DbWeather : IWeatherForecast
{
public override WeatherData GetWeather(Location location, DateTime timestamp)
{...}
}
class WeatherFactory
{
public IWeatherForecast GetWeatherProvider()
{...}
}
// Business logic independent from any DB
class MaritimeRoutePlanner
{
private IWeatherForecast weatherProvider = weatherFactory.GetWeatherProvider();
public bool ShouldAvoidLocation(Location location, DateTime timestamp)
{
WeatherData weather = weatherProvider.GetWeather(location, timestamp);
if(weather.Beaufort > 8)
return true;
else...
...
}
}
Run Code Online (Sandbox Code Playgroud)
如何在Haskell中实现此逻辑?
实际上,“纯逻辑” MaritimeRoutePlanner称之为weatherProvider.GetWeather()“纯IO”。
Haskell有可能吗?您如何在Haskell中建模?
Mar*_*ann 12
常见问题(如何从纯函数调用不纯函数)是一个常见问题解答。参见例如此问题及其答案:如何从不纯方法中返回纯值
与其他任何与软件体系结构相关的主题一样,如何以更具功能性的方式构造代码也取决于环境。您正在编写哪种程序?REST API?智能手机应用程序?控制台程序?批处理工作?外接程序?
在许多情况下,您可以摆脱所谓的“ 不纯净”三明治:
在Haskell中,您可以执行此操作,因为入口点始终是不纯的。这是天气决策问题的简单示意图。首先定义要使用的数据。在这里,我仅包含该beaufort值,但我假设WeatherData将包含比该值更多的数据(这就是为什么我将其定义为data而不是a的原因newtype)。
data WeatherData = WeatherData { beaufort :: Int } deriving (Eq, Show)
Run Code Online (Sandbox Code Playgroud)
您现在可以将决策逻辑编写为纯函数:
shouldAvoidLocation :: WeatherData -> Bool
shouldAvoidLocation weather = beaufort weather > 8
Run Code Online (Sandbox Code Playgroud)
加载数据是一个完全具体的操作:
readWeatherFromDb :: Location -> LocalTime -> IO WeatherData
readWeatherFromDb location timestamp = -- implementation goes here...
Run Code Online (Sandbox Code Playgroud)
这里没有明确的抽象。该函数读取数据并返回不纯数据。那可能是不纯净三明治中的第一步(不纯净)。
现在可以根据该体系结构来构造应用程序的入口点:
main :: IO ()
main = do
w <- readWeatherFromDb Berlin $ LocalTime (fromGregorian 2019 8 29) (TimeOfDay 8 55 8)
if shouldAvoidLocation w
then putStrLn "Avoid"
else putStrLn "Go"
Run Code Online (Sandbox Code Playgroud)
呼叫shouldAvoidLocation是在三明治中间的好东西,然后是不纯净的putStrLn呼叫。
简而言之,您不会从不纯的“函数”(也称为action)中提取数据;您将纯功能推向新的动作。
data WeatherData = WeatherData { beaufort :: Int, ... }
-- getWeather is a pure function
-- getWeather someLocation someDate is an action
getWeather :: Location -> DateTime -> IO WeatherData
getWeather l d = ...
-- badWeather is a pure function
badWeather :: WeatherData -> Bool
badWeather wd = beaufort wd > 8
-- avoidLocation is a pure function
-- avoidLocation someLocation someDate is an action
-- We can simply use fmap to lift (or wrap) the pure function badWeather
-- into a new action.
avoidLocation :: Location -> DateTime -> IO Bool
avoidLocation l d = fmap badWeather (getWeather l d)
Run Code Online (Sandbox Code Playgroud)
avoidLocation实际上不会产生布尔值;它创建,最后执行时的动作的用途 badWeather,以产生一个布尔值。