Battery Generator in F #

In my quest to learn more about F #, I tried to implement a “battery generator” as described by Paul Graham here . Our best solution is still fully dynamically typed:

open System

let acc (init:obj) : obj->obj=
  let state = ref init
  fun (x:obj) ->
    if (!state).GetType() = typeof<Int32>
       && x.GetType() = typeof<Int32> then
      state := (Convert.ToInt32(!state) + Convert.ToInt32(x)) :> obj
    else
      state := (Convert.ToDouble(!state) + Convert.ToDouble(x)) :> obj
    !state

do
  let x : obj -> obj = acc 1  // the type annotation is necessary here
  (x 5) |> ignore
  printfn "%A" (x 2)   // prints "8"
  printfn "%A" (x 2.3) // prints "10.3"

I have three questions:

  • If I remove the type annotation for x, the code does not compile because the compiler infers the type int -> objfor x - although it is accannotated to return obj->obj. Why is this and I can avoid it?
  • Any ideas for improving this dynamically typed version?
  • Is it possible to implement this with the correct static types? Maybe with member restrictions? (This is possible in Haskell, but not in OCaml, AFAIK)
+3
4

, F #, " ", Paul Graham.

. Lisp , , , , , Lisp .

F #, (, type number = Int of int | Float of float), . :

let add (x: obj) (y: obj) =
  match x, y with
  | (:? int as m), (:? int as n) -> box(m+n)
  | (:? int as n), (:? float as x)
  | (:? float as x), (:? int as n) -> box(x + float n)
  | (:? float as x), (:? float as y) -> box(x + y)
  | _ -> failwith "Run-time type error"

let acc x =
  let x = ref x
  fun (y: obj) ->
    x := add !x y
    !x

let x : obj -> _ = acc(box 1)
do x(box 5)
do acc(box 3)
do printfn "%A" (x(box 2.3))

. , , , . , , ?

:

let x = 1
let x = x + 5
ignore(3)
let x = float x + 2.3

x . . , ...

+8

, , F #. , , . inline , float:

let inline acc x = 
  let x = ref (float x)
  fun y ->
    x := (float y) + !x
    !x

:

val inline acc :
   ^a -> ( ^b -> float)
    when  ^a : (static member op_Explicit :  ^a -> float) and
          ^b : (static member op_Explicit :  ^b -> float)

, float. LISP ( ) , float ( ). - :

> acc 1 2;;            // For two integers, it returns float
val it : float = 3.0
> acc 1 2.1;;          // integer + float
val it : float = 3.1
> acc 1 "31";;         // It even works with strings!
val it : float = 32.0
+6

. , Haskell, .

+3

The problem with trying to do this using static typing is to add two different numbers, possibly different types, while preserving the type of the left side. As John Harrop says, this is possible with a type of union. Once you have determined the type of merge and the corresponding add operation that works as mentioned, the actual battery is very simple. My implementation:

module MyTest

type Numeric =
  | NInt of int
  | NFloat of float

  member this.Add(other : Numeric) : Numeric =
    match this with
      | NInt x ->
        match other with
          | NInt y -> NInt (x + y)
          | NFloat y -> NInt (x + (int y))
      | NFloat x ->
        match other with
          | NInt y -> NFloat (x + (float y))
          | NFloat y -> NFloat (x + y)

  override this.ToString() =
    match this with
      | NInt x -> x.ToString()
      | NFloat x -> x.ToString()

let foo (n : Numeric) =
  let acc = ref n
  fun i ->
    acc := (!acc).Add(i)
    !acc

let f = foo (NFloat 1.1)
(2 |> NInt |> f).ToString() |> printfn "%s"
0
source

Source: https://habr.com/ru/post/1763269/


All Articles