Создать и использовать реестр функций в F # - PullRequest
2 голосов
/ 04 июля 2010

Я часто использую текущий шаблон кода в программе, которую я сейчас пишу:

let test_titles = ["a_fault"; "b_fault"; "c_fault"]
let tests: (unit -> 'component option) list = [a_fault; b_fault; c_fault]
let test_registry = List.zip test_titles tests
let apply_test (title, test) = test () |> Option.map (fun s -> (title, s))
let result: (string * 'component) option = test_registry |> List.tryPick apply_test

, чтобы иметь реестр тестов, который идентифицирует неисправные компоненты и тип ошибки, которая, как оказалось, являетсятакое же имя, как и у имени функции.

  1. Есть ли лучший способ создать этот test_registry, желательно, чтобы я не писал имена тестов вручную (динамически получая имя функции)?

  2. Как правило, это идиоматический F #?

Редактировать : в последней строке кода была ошибка.Результат вычисляется с использованием test_registry, а не tests.

Ответы [ 2 ]

2 голосов
/ 04 июля 2010

Это кажется неплохим, но другой вариант, который стоит рассмотреть, - это чтобы тесты знали свои собственные имена.Прямо сейчас у вас есть (я предполагаю)

type Test<'comp> = unit -> 'comp option

, и вы могли бы вместо

type Test<'comp> = unit -> string * 'comp option

, где строка является именем.

У меня нетздравый смысл вашей «большой картины», чтобы дать другой совет или знать, имеет ли это смысл.

Просто добавьте его, если тесты являются частью какого-то модуля M, вы можете использовать (?)оператор, так что, например, M?foo имеет доступ как к функции тестирования, так и к ее имени.

1 голос
/ 04 июля 2010

Одним из способов избежать необходимости явного написания названия теста в коде (в виде строки) было бы использование кавычек.Вместо того, чтобы создавать список функций и список строк, вы можете создать список «указанных» значений функций.Затем вы можете написать код, который обрабатывает цитаты и дает вам все необходимое.

Я предполагаю, что ваши тесты выглядят примерно так, как показано ниже (функции, получающие единицу и возвращающие некоторое значение в результате).Список будет построен следующим образом:

let test_a () = Some 32
let test_b () = None
let tests = [ <@ test_a @>; <@ test_b @> ]

Затем вы можете написать код, подобный этому, чтобы получить информацию о тесте:

open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns

let getInfo (e:Expr<unit -> 'R>) = // '
  match e.Raw with 
  // This expects that the quotation contains a reference to a global
  // function (in some module) that takes unit as the parameter
  | Lambda(a, Call(_, m, _)) -> 
      // Return a tuple containing a name of the test (string) and
      // a function that invokes it (note that the invocation will be 
      // a bit slow as we use reflection here)
      m.Name, (fun () -> m.Invoke(null, [| |]) :?> 'R) // ' (*)
  // Ohter quotations will cause an exception
  | _ -> failwith "unexpected quotation"

Вот пример, как вы могли бы использовать это:1009 *

let e = <@ test_a @>    
let s, f = getInfo e // gives 'string * (unit -> int option)

// Your original code could be written like this:
tests |> List.map getInfo |> List.tryPick (fun (title, test) ->
  test () |> Option.map (fun s -> (title, s)))

В качестве альтернативы вы можете изменить строку (*), чтобы получить функцию, которая возвращает имя теста и результат, что устраняет необходимость в Option.map:

// An alternative version of the marked line in the 'getInfo' function
(fun () -> m.Name, m.Invoke(null, [| |]) :?> 'R) // ' (*)

// Then you can write just:
tests |> List.map getInfo |> List.tryPick (fun test -> test())
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...