使用构造函数时,SML是否可以在参数上运行函数?

问题描述

我正在学习SML,并尝试创建一个名为mySet的数据类型,该数据类型可以是任何整数或实数列表,但没有重复项且按顺序排列。到目前为止,我已经制作了数据类型和一些执行我需要的功能的函数,然后将其返回到该数据类型中可以正常工作的列表。但是我意识到,也可以使用数据类型的构造函数来完全绕过需求。对于我所需要的,我可以只使用该函数,但是我真的很想知道是否有什么办法可以解决该问题?如果列表不符合要求,那么我针对该数据类型的大多数功能将无法正常工作。

datatype 'a set = Set of 'a list | Empty;

(* takes (item,list) and removes any copies of item from list *)
fun cleanList(a,[]) = []
    |cleanList(a,b::rest) =
        if b = a then cleanList(a,rest)
        else
            b::cleanList(a,rest);

(*uses cleanList to make a list with all the items,no copies*)
fun removeDup([]) = []
| removeDup(a::rest) =
    let
        val cleanRest = cleanList(a,rest);
    in
        a::removeDup(cleanRest)
    end;


(*uses above 2 functions,then puts the list in order *)
fun makeSet([]) = Empty
    |makeSet(inputList) =
        let
            val cleanList = removeDup(inputList)
            val sortedList = ListMergeSort.sort (fn(x,y) => x > y) cleanList;
        in
            Set(sortedList)
        end;
        

val testList = [27,81,27,3,4,5,7];

makeSet(testList); (* returns Set [3,7,81] *)

Set([1,1,1]); (*Set [1,1] which I don't want to allow *)

解决方法

我意识到,也可以使用数据类型的构造函数来完全绕过要求。对于我所需要的,我可以只使用该函数,但是我真的很想知道是否有什么办法可以解决该问题?

有!您的基本构造函数将破坏数据类型的不变式,因此您想隐藏它,而只公开smart constructor,它在某些输入上故意失败,并且不允许无效状态。

正如molbdnilo所说,之所以称为abstract type,是因为您隐藏了实现方式并通过其智能构造函数接口公开了它,该接口具有您想要的任何行为。您也可以将其称为opaque type

实现此目标的每种方法的共同点是,您有一个局部范围,在该范围内声明了数据类型,但是仅保留了智能构造函数的外部接口。为了探索所需的语言功能,我尝试简单地写:

val (fmap,pure) =
  let
    datatype 'a maybe = Just of 'a | Nothing
    fun fmap f Nothing = Nothing
      | fmap f (Just x) = Just (f x)
    fun pure x = Just x
  in (fmap,pure)
  end

但是我的SML编译器实际上拒绝了该程序:

!   in (fmap,pure)
!       ^^^^
! Type clash: expression of type
!   ('a -> 'b) -> 'a maybe -> 'b maybe
! cannot have type
!   'c
! because of a scope violation:
! the type constructor maybe is a parameter 
! that is declared within the scope of 'c

因此,我们需要了解专门为此目的设计的SML语言功能之一:


更新:@ruakh指出我忘记了local

以下是使用local-in-end的示例:

local
  datatype 'a maybe = Just of 'a | Nothing
in
  fun fmap f Nothing = Nothing
    | fmap f (Just x) = Just (f x)
  fun pure x = Just x
end

两个函数fmappure之间共享数据类型,但是定义从外部接口隐藏。您可以具有多个本地定义,包括辅助函数。

并且构造函数是隐藏的:

> New type names: =maybe
  val ('a,'b) fmap = fn : ('a -> 'b) -> 'a maybe -> 'b maybe
  val 'a pure = fn : 'a -> 'a maybe

letlocalDifference between "local" and "let" in SML

中进一步讨论

以下是使用abstype的同一示例:

abstype 'a maybe = Just of 'a | Nothing
with
  fun fmap f Nothing = Nothing
    | fmap f (Just x) = Just (f x)
  fun pure x = Just x
end

您会看到JustNothing的隐藏方式:

> New type names: maybe
  type 'a maybe = 'a maybe
  val ('a,'b) fmap = fn : ('a -> 'b) -> 'a maybe -> 'b maybe
  val 'a pure = fn : 'a -> 'a maybe

但是您也可以使用不透明的模块。 This StackOverflow answer精确地介绍了集合函子的骨架如何工作。 ML for the Working Programmer的第7章介绍了如何定义以模块作为参数的模块(仿函数)。这是参数多态性的替代方法。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...