Как сделать переход от obj к опции <obj>? - PullRequest
10 голосов
/ 09 июня 2011

У меня есть функция, которая принимает параметр типа object и должна уменьшить его до option<obj>.

member s.Bind(x : obj, rest) =
    let x = x :?> Option<obj>

Если я передам (например) Option<string> как x,последняя строка выдает исключение: Невозможно привести объект типа «Microsoft.FSharp.Core.FSharpOption'1 [System.String]» к типу «Microsoft.FSharp.Core.FSharpOption'1 [System.Object] '.

Или, если я попытаюсь проверить тип:

member s.Bind(x : obj, rest) =
   match x with
    | :? option<obj> as x1 -> ... // Do stuff with x1
    | _ -> failwith "Invalid type"

, тогда x никогда не совпадет с option<obj>.

.В настоящее время мне нужно указать тип, который содержит опция (например, если функции передано option<string>, и я понижаю параметр до этого значения, а не option<obj>, функция работает.

Есть ли способЯ могу уменьшить значение параметра до option<obj>, не указав тип этого параметра? Я пробовал option<_>, option<#obj> и option<'a> с теми же результатами.

В качестве фона,параметр должен иметь тип obj, потому что я пишу интерфейс для монады, поэтому Bind должен связывать значения diffРазличные типы в зависимости от монады, которая реализует интерфейс.Эта конкретная монада является монадой продолжения, поэтому она просто хочет убедиться, что параметр равен Some(x), а не None, затем передайте x на отдых.(Причина, по которой мне нужен интерфейс, заключается в том, что я пишу монадный преобразователь, и мне нужен способ сказать ему, что его монады параметров реализуют связывание и возврат.)

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

Ответы [ 3 ]

7 голосов
/ 09 июня 2011

Нет хорошего способа решить эту проблему в настоящее время.

Проблема в том, что вам нужно будет ввести новый параметр универсального типа в сопоставление с образцом (при сопоставлении с option<'a>), но F # позволяет вам только определять параметры универсального типа в объявлениях функций. Таким образом, ваше единственное решение - использовать некоторые трюки Reflection. Например, вы можете определить активный шаблон, который скрывает это:

let (|SomeObj|_|) =
  let ty = typedefof<option<_>>
  fun (a:obj) ->
    let aty = a.GetType()
    let v = aty.GetProperty("Value")
    if aty.IsGenericType && aty.GetGenericTypeDefinition() = ty then
      if a = null then None
      else Some(v.GetValue(a, [| |]))
    else None

Это даст вам None или Some, содержащие obj для любого типа параметра:

let bind (x : obj) rest =   
    match x with    
    | SomeObj(x1) -> rest x1
    | _ -> failwith "Invalid type"

bind(Some 1) (fun n -> 10 * (n :?> int))
2 голосов
/ 09 июня 2011

Я не уверен, почему вам нужно получить ваш ввод как obj, но если ваш ввод - это Option <_>, тогда это просто:

member t.Bind (x : 'a option, rest : obj option -> 'b) =
    let x = // val x : obj option
        x
        |> Option.bind (box >> Some)
    rest x
1 голос
/ 09 июня 2011

Чтобы ответить на ваш последний вопрос: вы можете использовать небольшое изменение кода Томаса, если вам нужен универсальный способ проверки параметров без значений в боксе заранее:

let (|Option|_|) value = 
  if obj.ReferenceEquals(value, null) then None
  else
    let typ = value.GetType()
    if typ.IsGenericType && typ.GetGenericTypeDefinition() = typedefof<option<_>> then
      let opt : option<_> = (box >> unbox) value
      Some opt.Value
    else None
//val ( |Option|_| ) : 'a -> 'b option    

let getValue = function
  | Option x ->  x
  | _ -> failwith "Not an option"

let a1 : int = getValue (Some 42)
let a2 : string = getValue (Some "foo")
let a3 : string = getValue (Some 42) //InvalidCastException
let a4 : int = getValue 42 //Failure("Not an option")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...