给出了一个函数
f: A -> Maybe B.
。
那么函数
isProper : A -> Bool
可以定义为
isProper a =
case f a of
Just _ ->
True
Nothing ->
False
并让有一个类型包装 A,
type alias ProperA =
ProperA A
只能由函数生成
fromA: A -> Maybe ProperA
fromA a =
if isProper a then
Just (ProperA a)
else
Nothing
我想要得到的是函数
f0 : ProperA -> B
,其中
Just (f0 (ProperA a)) == f a
是真的。 (f0 不必完全采用这种形式;我想从类似 A 的类型值获取 B 类型值,而不使用 case 表达式)
当我尝试使用像
String.uncons
这样的函数时,当我已经知道字符串确保非空时,就会发生这个问题,因为字符串是用像String.cons
这样的函数构造的。
我多次遇到这样的问题,所以我想要一个通用的解决方案。
简单来说,我已经想到了
f0 (ProperA a) =
case (f a) of
Just b ->
b
Nothing ->
Debug.todo "No way!"
但是对于永远不会发生的情况,还有比使用 Debug.todo 更好的方法吗? 或者,有没有其他方法可以在不生成这样的函数的情况下解决问题? (核心问题是让不可能的状态变得不可能。)
可选,这个问题在其他函数式语言中如何解决?
您要求一种类型,可以让您在类型系统中编码某件事是真的,以便您的函数始终可以工作,并且您不需要 Maybe。
使不可能的状态变得不可能的一般方法是滚动代表所有可能状态的自己的(总和)类型。
如果你想要一个明确有第一个字符的字符串,你可以定义
type NonEmptyString = NES Char String
toString : NonEmptyString -> String
toString NES c s =
String.cons c s
您可以对列表等进行类似的操作,正如 @naïm-favier 所说,有很多软件包可以为您完成非空的事情。
Debug.todo
的想法并不令人满意,您可以根据您的观点,通过仅使用目标类型的虚拟值来使其变得更好或更差,例如
unsafeHead : ProperA String -> Char
unsafeHead (ProperA string) =
case String.uncons string of
Just (h,_) ->
h
Nothing ->
'∄'
如果将
ProperA
设为不透明类型(elm-radio Episode - 不公开类型构造函数,仅公开 fromA
函数),那么 '∄'
答案永远不会出现。
您似乎正在尝试为一般问题制定通用解决方案,这是程序员通常热衷于做的事情。但是让不可能的状态变得不可能的教训不是为子类型编写库!它是为了创建特定于领域、特定于问题的数据类型。类型如
type Size = Standard | Narrow
type User = NotLoggedIn | LoggedIn Key Profile | SuperUser Key Powers
不要使用通用类型,而是针对您的问题。您可以稍后添加变体。您可以根据您的问题处理具体案例。
一概而论!使您的解决方案针对您正在解决的问题。在 elm 中创建新数据类型非常便宜,因此您应该一直这样做。如果您创建了特定于问题的数据类型,以后更改它将会非常容易。相信我。与维护其他代码库相比,维护针对特定问题的 elm 代码是一个梦想。如果您创建了过度概括的数据类型,那么您将陷入平衡库的理论需求与项目的实际需求之间的困境,并且陷入将通用库弯曲到特定问题的需求的困境。最好创建针对特定问题的数据类型,并放弃过早的面向未来的做法。
(我希望我的建议能够更具体地针对您的问题,但我只有一个您想要做的事情的例子,但这是很好的通用榆树建议。)