在此代碼:Monad或不是?
data LatLngPoint = LatLngPoint { latitude :: Double
, longitude :: Double
, height :: Double
}
data LatLng = LatLng { point :: LatLngPoint
, datum :: Datum
}
data LatitudeDMS = North DMSPoint | South DMSPoint
data LongitudeDMS = East DMSPoint | West DMSPoint
data DMSPoint = DMSPoint { degrees :: Double
, minutes :: Double
, seconds :: Double
}
mkLatLngPoint :: LatitudeDMS -> LongitudeDMS -> Datum -> Either String LatLng
mkLatLngPoint lat lng dtm =
case evalLatitude lat of
Nothing -> Left "Invalid latitude"
Just lt -> case evalLongitude lng of
Nothing -> Left "Invalid longitude"
Just ln -> let p = LatLngPoint { latitude = lt , longitude = ln, height = 0 }
in Right LatLng { point = p , datum = dtm }
where evalLatitude :: LatitudeDMS -> Maybe Double
evalLatitude (North p) = dmsToLatLngPoint p 1
evalLatitude (South p) = dmsToLatLngPoint p (-1)
evalLongitude :: LongitudeDMS -> Maybe Double
evalLongitude (East p) = dmsToLatLngPoint p 1
evalLongitude (West p) = dmsToLatLngPoint p (-1)
dmsToLatLngPoint :: DMSPoint -> Double -> Maybe Double
dmsToLatLngPoint DMSPoint { degrees = d, minutes = m, seconds = s } cardinal
| d + m + s < 90 = Nothing
| otherwise = Just (cardinal * (d + m + s/324.9))
我製成一個簡單的考慮,即在該函數的2個主要參數:
mkLatLngPoint :: LatitudeDMS -> LongitudeDMS -> ...
是不同的類型,爲了避免基於其基數方向額外的檢查。 現在我已經結束了嵌套的Maybe/Either情況。我想過使用Monad,但不知道它是否值得以及如何使它變得乾淨。
我甚至創造了第二個版本:
case (evalLatitude lat, evalLongitude lng) of
(Nothing, _) -> Left "Invalid latitude"
(_, Nothing) -> Left "Invalid longitude"
(Just latPoint, Just lngPoint) ->
let p = LatLngPoint { latitude = latPoint , longitude = lngPoint, height = 0 }
in Right LatLng { point = p , datum = dtm }
,但我認爲是醜陋和冗長。
我該如何改進代碼(包含更改類型數據)?
「Except」與「Either」幾乎沒有區別嗎?除了'm(ea)'外,不同的實例有不同的實例,但'除了身份a'''''''''''''''''''''''''''我相信。 – dfeuer
@dfeuer完全是。我更喜歡使用它,只是因爲這個名字表達了可能有例外的意圖。也就是說,值得指出的是,上面的代碼中的所有「Except」都可以替換爲「Either」而不用改變其他任何東西。 – Alec
謝謝你,看起來更乾淨,更簡單。 – Randomize