Streaming 方法类型不兼容

Streaming 方法类型不兼容,streaming,ocaml,Streaming,Ocaml,我最近试着玩一个类似肥皂的东西。。。有些代码使用OCaml类和C库进行编码,比如lame(通过OCaml lame)等 (=相同接口) 某个地方有两个高级类,它们继承自两个独立的encoderbase虚拟类: (* Mp3_output module *) class virtual encoderbase = object (self) method encode ncoder channels buf offset size = if channels = 1

我最近试着玩一个类似肥皂的东西。。。有些代码使用OCaml类和C库进行编码,比如lame(通过OCaml lame)等

(=相同接口)

某个地方有两个高级类,它们继承自两个独立的
encoderbase
虚拟类:

(* Mp3_output module *)
class virtual encoderbase =
    object (self)
    method encode ncoder channels buf offset size =
        if channels = 1 then
            Lame.encode_buffer_float_part ncoder buf.(0) buf.(0) offset size
        else
            Lame.encode_buffer_float_part ncoder buf.(0) buf.(1) offset size
end

(* somewhere in the code *)
class to_shout sprop =
    (* some let-s *)
    object (self)
    inherit
        [Lame.encoder] Icecast2.output ~format:Format_mp3 (* more params *) as super
    inherit base
    (* ... *)
end

在以下情况下一切正常:

let icecast_out source format =
    let sprop =
        new Mp3_output.shout_sprop
    in
        (* some code here *)
        new Mp3_output.to_shout sprop
但当我尝试这样的事情时:

let icecast_out source format =
    let sprop =
        if format = Format_other then
            new Other_output.shout_sprop
        else
            new Mp3_output.shout_sprop
    in
        (* some code here *)
        if format = Format_mp3 then
            new Mp3_output.to_shout sprop
        else
            new Other_output.to_shout sprop
编译中断,出现错误@
新的其他输出。要显示sprop

Error: This expression has type Other_output.to_shout
    but an expression was expected of type Mp3_output.to_shout
    Types for method encode are incompatible
有没有办法“说服”OCaml(公共祖先?包装类?类型转换?)同时使用这两个不同的类/绑定进行编译

更新(2015年12月15日): 代码示例:

有没有办法“说服”OCaml(公共祖先?包装类?类型转换?)同时使用这两个不同的类/绑定进行编译

人们相信OCaml是一种类型安全的语言,因此不可能说服OCaml编译一个会崩溃的程序,除非您使用了一些不安全的方法

您的示例中的以下代码片段说明了误解的根源:

type 'a term = Save of 'a
let enc_t =
  if format_num = 1
  then Save Lame.encoder
  else Save Other.encoder
表达式
Save Lame.encoder
具有类型
Lame.encoder term
,而表达式
Save Other.encoder
具有类型
Other.encoder term
。从类型系统的角度来看,这是两种完全不同的类型,尽管它们是由相同的类型构造函数
term
构建的。C.f.,
int list
float list
是不同的类型,您不能为这两种不同类型的变量值赋值。这不是OCAM本身的一个属性,这是任何参数多态性的一个属性,例如,<>代码> STD::vector < /COD>和 STD::vector 是不同的表示类型,它们的值不能互换使用,尽管严格地说C++中的模板不是真参数多态性,它们只是宏

但是回到OCaml。多态函数背后的思想是,具有多态数据类型的参数具有相同的表示形式,并且函数可以应用于此类型的任何实例,而无需任何运行时检查,因为所有类型信息都已删除。比如说,

let rec length = function
  | [] -> 0
  | _ :: xs -> 1 + length xs
是多态的,因为它可以应用于多态数据类型的任何实例
'a list
,包括
int list
float list
person list
,等等

另一方面,函数

let rec sum = function 
  | [] -> 0
  | x :: xs -> x + sum xs
不是多态的,只能应用于
int list
类型的值。原因是这个函数的实现依赖于一个事实,即每个元素都是一个整数。如果您能够说服类型系统,将此函数应用于
float
列表,您将得到一个分段错误

但你可能会说,我们遗漏了一些东西,因为
float
list的求和函数看起来基本相同:

let rec fsum = function 
  | [] -> 0.
  | x :: xs -> x +. fsum xs
因此,有机会抽象出一个总结。当我们抽象某些东西时,我们会发现不同实现之间的差异,并将其抽象出来。OCaml中最简单的抽象原语是一个函数,所以让我们这样做:

let rec gsum zero plus xs =
  let (+) = plus in
  let rec sum = function
    | [] -> zero
    | x :: xs -> x + sum xs in
  sum xs
我们抽象出
zero
元素和
plus
函数。所以我们得到了一个求和的抽象,它适用于任何类型,你可以为它提供一个
操作和元素中立的操作(在抽象代数中称为环数据结构)。gsum的类型为

'a -> ('b -> 'a -> 'a) -> 'b list -> 'a
它甚至太通用了,我们可以将它专门化一点,就像这种类型一样

'a -> ('a -> 'a -> 'a) -> 'a list -> 'a
更合适。我们可以将其聚合为记录类型,而不是逐个传递环结构的元素:

type 'a ring = {
    zero : 'a;
    plus : 'a -> 'a -> 'a
}
并按如下方式实施我们的通用
sum

let rec gsum ring xs =
  let (+) = ring.plus in
  let rec sum = function
    | [] -> ring.zero
    | x :: xs -> x + sum xs in
  sum xs
在这种情况下,我们将有一个很好的类型
sum:'a ring->'a list->'a
。在某个时候,您会发现自己用新字段扩展了这个记录,并实现了越来越多的函数,这些函数接受这个环结构作为第一个参数。这将是一个很好的时间来使用一个更重的抽象,叫做
functor
functor
实际上是一个类固醇记录,隐式地传递给functor实现的每个函数。除了函数,函数和函子的记录还有其他抽象技术:第一类模块、对象、类,很快还有隐式(也称为类型类)。学习如何选择更适合于每个特定情况的抽象技术是编程专业知识的一部分。一般的建议是尽可能使用最轻的。事实上,95%的用户使用函数或函数记录就足够了

现在让我们回到你的例子。你在同一个洞里击球, 你把多病态和抽象混淆了:

let fmt =
  if format_num = 1
  then new Lamefmt.sout sp
  else new Otherfmt.sout sp in
Lamefmt.sout
Otherfmt.sout
是不同的类型,第一种类型有:

type sout = <
  doenc  : Lame.encoder -> float array array -> int -> int -> string;
  encode : Lame.encoder -> float array array -> int -> int -> string 
>
在哪里

然后我们可以构造不同的编码器:

let lame : encoder = 
   let encoder = Lame.create_encoder () in
   Lame.encode_buffer_float_part encoder

let other : encoder = 
   let encoder = Other.create_encoder () in 
   Other.encode_buffer_float_part encoder
module Lame = struct
  type encoder
  let encode_buffer_float_part : encoder -> float -> unit = fun _ -> failwith "ocaml_lame_encode_buffer_float"
end

module Otherencoder = struct
  type encoder
  let encode_buffer_float_part : encoder -> float -> unit = fun _ -> failwith "ocaml_otherencoder_encode_buffer_float"
end

module Mp3_output = struct
  class to_shout = object
    method encode ncoder x =
      Lame.encode_buffer_float_part ncoder x
  end
end

module Other_output = struct
  class to_shout = object
    method encode ncoder x =
      Otherencoder.encode_buffer_float_part ncoder x
  end
end

type format = Format_other | Format_mp3

let icecast_out source format =
  if format = Format_mp3 then new Mp3_output.to_shout
  else new Other_output.to_shout
然后你可以交替使用这两个值。有时,不同的编码器需要不同的参数,在这种情况下,我们的任务是尽快发送它们,例如

 let very_customizable_encoder x y z : encoder = 
   let encoder = VCE.create_encoder x y z in 
   Other.encode_buffer_float_part encoder
在这种情况下,您应该尽可能靠近用户解决定制问题,然后再使用抽象

通常使用哈希表或其他关联数据结构来存储编码器。这种方法甚至允许您表示一个插件体系结构,其中插件是动态加载的,并在某些表中注册它们自己(类型编码器的值)

结语。用一个函数来表示您的问题就足够了。也许在某个时候,您需要使用函数的记录。到目前为止,我认为没有必要使用类。通常,当您对开放递归感兴趣时,也就是说,当您的问题由一组相互递归f表示时,它们是必需的
type encoder = buffer -> buffer -> int -> int -> string 
type buffer = float array array
let lame : encoder = 
   let encoder = Lame.create_encoder () in
   Lame.encode_buffer_float_part encoder

let other : encoder = 
   let encoder = Other.create_encoder () in 
   Other.encode_buffer_float_part encoder
 let very_customizable_encoder x y z : encoder = 
   let encoder = VCE.create_encoder x y z in 
   Other.encode_buffer_float_part encoder
module Lame = struct
  type encoder
  let encode_buffer_float_part : encoder -> float -> unit = fun _ -> failwith "ocaml_lame_encode_buffer_float"
end

module Otherencoder = struct
  type encoder
  let encode_buffer_float_part : encoder -> float -> unit = fun _ -> failwith "ocaml_otherencoder_encode_buffer_float"
end

module Mp3_output = struct
  class to_shout = object
    method encode ncoder x =
      Lame.encode_buffer_float_part ncoder x
  end
end

module Other_output = struct
  class to_shout = object
    method encode ncoder x =
      Otherencoder.encode_buffer_float_part ncoder x
  end
end

type format = Format_other | Format_mp3

let icecast_out source format =
  if format = Format_mp3 then new Mp3_output.to_shout
  else new Other_output.to_shout
Error: This expression has type Other_output.to_shout
       but an expression was expected of type Mp3_output.to_shout
       Types for method encode are incompatible
module Mp3_output = struct
  class to_shout ncoder = object
    method encode x =
      Lame.encode_buffer_float_part ncoder x
  end
end

module Other_output = struct
  class to_shout ncoder = object
    method encode x =
      Otherencoder.encode_buffer_float_part ncoder x
  end
end

type format = Format_other | Format_mp3

let icecast_out source format =
  if format = Format_mp3 then new Mp3_output.to_shout Lame.ncoder
  else new Other_output.to_shout Otherencoder.ncoder