2010-02-12 13 views
19

decir que quiero saber si F # tiene una función de biblioteca de tipo¿La documentación de F # tiene una forma de buscar funciones por sus tipos?

('T -> bool) -> 'T list -> int 

es decir, algo que cuenta el número de elementos de una lista que una función devuelve cierto para. (o devuelve el índice del primer elemento que devuelve verdadero)

Solía ​​usar la lista grande en el sitio MSR para F # antes de que estuviera lista la documentación en MSDN. Solo podía buscar en la página el texto anterior porque los tipos estaban en la lista. Pero ahora la documentación de MSDN solo enumera los tipos en las páginas individuales: la página del módulo contiene un montón de texto descriptivo. Google trabaja un poco-sorta, pero no puede ayudar con

// compatible interfaces 
('T -> bool) -> Seq<'T> -> int 
// argument-swaps 
Seq<'T> -> ('T -> bool) -> int 
// type-variable names 
('a -> bool) -> Seq<'a> -> int 
// wrappers 
('a -> bool) -> 'a list -> option<int> 
// uncurried versions 
('T -> bool) * 'T list -> int 
// .NET generic syntax 
('T -> bool) -> List<'T> -> int 
// methods 
List<'T> member : ('T -> bool) -> int 

Haskell tiene un programa independiente para este llamado Hoogle. ¿F # tiene un equivalente, como Fing o algo así?

Respuesta

10

Basado en la respuesta de kvb, creé una aplicación completa. Está alojado en github en http://github.com/sandersn/fing.

El código sigue siendo bastante feo, pero funciona para casos simples. Saqué por el momento el más general-unificador de kvb (mgu) porque agrega muchos resultados no obvios. Las cosas de lujo como las restricciones estructurales y el supertipo general tampoco funcionan todavía.

También hay una versión binaria para la línea de comandos si no desea compilar desde el código fuente. (Todavía requiere una versión moderna del .NET runtime instalado, sin embargo.) Eventualmente encontraré algo de hosting ASP.NET, aprenderé ASP, y lo envolveré todo en una aplicación web para que no se necesite ninguna instalación. (Creo que si hay demanda podría crear una GUI del lado del cliente, pero tengo incluso menos experiencia con ese tipo de cosas.)

17

No conozco ninguna de estas herramientas. Sin embargo, puede ser un ejercicio divertido escribir uno usando System.Reflection (o mejor aún, la biblioteca de metadatos en el PowerPack), para que pueda tener en cuenta nombres de variables de tipo de módulo de equivalencia, etc.

EDIT - Tenía razón - es era un ejercicio divertido. Lo que sigue tiene muchas verrugas, pero no es tan malo para ~ 150 líneas de código. Con suerte, esto será suficiente para comenzar a alguien que quiera trabajar en una herramienta real. No hace nada avanzado, como buscar funciones con parámetros reordenados, y la biblioteca de metadatos es un poco exigente con el uso de nombres completamente calificados, por lo que debe ser un poco cuidadoso. Para responder a la pregunta en su puesto original, ejecuté

find "('a -> Microsoft.FSharp.Core.bool) -> Microsoft.FSharp.Collections.list`1<'a> -> Microsoft.FSharp.Core.int" 

y obtuvo la siguiente lista de candidatos:

Microsoft.FSharp.Core.Operators.(+) 
Microsoft.FSharp.Core.Operators.(-) 
Microsoft.FSharp.Core.Operators.(*) 
Microsoft.FSharp.Core.Operators.(/) 
Microsoft.FSharp.Core.Operators.(%) 
Microsoft.FSharp.Core.Operators.sqrt 
Microsoft.FSharp.Core.LanguagePrimitives.EnumOfValue 
Microsoft.FSharp.Core.LanguagePrimitives.EnumToValue 
Microsoft.FSharp.Core.LanguagePrimitives.AdditionDynamic 
Microsoft.FSharp.Core.LanguagePrimitives.CheckedAdditionDynamic 
Microsoft.FSharp.Core.LanguagePrimitives.MultiplyDynamic 
Microsoft.FSharp.Core.LanguagePrimitives.CheckedMultiplyDynamic 
Microsoft.FSharp.Core.LanguagePrimitives.GenericZero 
Microsoft.FSharp.Core.LanguagePrimitives.GenericOne 
Microsoft.FSharp.Collections.List.find 
Microsoft.FSharp.Collections.List.findIndex 
Microsoft.FSharp.Collections.List.maxBy 
Microsoft.FSharp.Collections.List.minBy 

De éstos, sólo List.findIndex tiene exactamente el tipo genérico que estás buscando, pero con la combinación correcta de parámetros de tipo, también lo hacen los demás (por ejemplo, si 'a = int entonces List.find tiene el tipo deseado). Desafortunadamente, las restricciones no se tienen en cuenta en la búsqueda, por lo que las funciones que no son List no pueden coincidir.

Sin más preámbulos, aquí está el código que utilicé: deberá agregar una referencia al ensamblado FSharp.PowerPack.Metadata para que funcione.

open Microsoft.FSharp.Metadata 
open System.Text.RegularExpressions 

(* type parameters let us switch out representation if need be *) 
type Tag<'ty> = | Tuple | Arr | Ground of 'ty 
type Ty<'ty,'a> = Param of 'a | Complex of Tag<'ty> * Ty<'ty,'a> list 

(* Gets something stable from an FSharpEntity so that we can see if two are identical *) 
let rec getType (e:FSharpEntity) = 
    if (e.IsAbbreviation) then 
    getType e.AbbreviatedType.NamedEntity 
    else 
    e.ReflectionType 

(* FSharpType -> Ty<System.Type,string> *) 
let rec cvt (e:FSharpType) = 
    if e.IsTuple then 
    Complex(Tuple, e.GenericArguments |> Seq.map cvt |> List.ofSeq) 
    elif e.IsFunction then 
    Complex(Arr, e.GenericArguments |> Seq.map cvt |> List.ofSeq) 
    elif e.IsGenericParameter then 
    Param e.GenericParameter.Name 
    else 
    Complex(Ground(e.NamedEntity |> getType), e.GenericArguments |> Seq.map cvt |> List.ofSeq) 

(* substitute type for variable within another type *) 
let rec subst v t = function 
| Complex(tag,l) -> Complex(tag, l |> List.map (subst v t)) 
| Param i when i = v -> t 
| Param j -> Param j 

(* get type variables used in a type *) 
let rec usedVars = function 
| Param i -> Set.singleton i 
| Complex(tag, l) -> Set.unionMany (List.map usedVars l) 

(* Find most general unifier (if any) for two types *) 
let mgu t1 t2 = 
    let rec mgu subs = function 
    | [] -> Some subs 
    | (Complex(tag1,l1),Complex(tag2,l2))::rest -> 
     if tag1 <> tag2 then 
     None 
     else 
     let rec loop r = function 
     | [],[] -> mgu subs r 
     | [],_ | _,[] -> None 
     | x::xs, y::ys -> loop ((x,y)::r) (xs,ys) 
     loop rest (l1,l2) 
    | (Param i, Param j)::rest when i = j -> mgu subs rest 
    | ((Param i, x) | (x, Param i))::rest -> 
     if (Set.contains i (usedVars x)) then 
     None (* type would be infinite when unifying *) 
     else 
     mgu ((i,x)::subs) (rest |> List.map (fun (t1,t2) -> (subst i x t1, subst i x t2))) 
    mgu [] [t1,t2] 

(* Active patterns for parsing - this is ugly... *) 
let (|StartsWith|_|) r s = 
    let m = Regex.Match(s, r) 
    if m.Success && m.Index = 0 then 
    Some(m.Value, s.Substring(m.Length)) 
    else None 

let rec (|Any|) (|P|_|) = function 
| P(x,Any (|P|_|) (l,r)) -> x::l, r 
| s -> [],s 

let rec (|Any1|_|) (|P|_|) = function 
| P(x,Any (|P|_|) (l,r)) -> Some(x::l, r) 
| _ -> None 

let (|Seq|_|) (|P|_|) (|Q|_|) = function 
| P(x,Q(y,r)) -> Some((x,y),r) 
| _ -> None 

let (|Choice|_|) (|P|_|) (|Q|_|) = function 
| P(p) -> Some p 
| Q(p) -> Some p 
| _ -> None 

let (|Delimit|_|) s (|P|_|) = function 
| P(x,Any ((|Seq|_|) ((|StartsWith|_|) s) (|P|_|)) (l,r)) -> Some(x::(List.map snd l), r) 
| _ -> None 

let (|Delimit1|_|) s (|P|_|) = function 
| P(x,StartsWith s (_,Delimit s (|P|_|) (l,r))) -> Some(x::l, r) 
| _ -> None 

(* Basically a BNF grammar for types *) 
let rec (|TyE|_|) = function 
| ArrE(p) | TupleE(p) | AtomE(p) -> Some(p) 
| _ -> None 
and (|ArrE|_|) = function 
| Choice (|TupleE|_|) (|AtomE|_|) (dom,StartsWith "->" (_,TyE(rng,r))) -> Some(Complex(Arr,[dom;rng]), r) 
| _ -> None 
and (|TupleE|_|) = function 
| Delimit1 @"\*" (|AtomE|_|) (l,r) -> Some(Complex(Tuple,l), r) 
| _ -> None 
and (|AtomE|_|) = function 
| ParamE(x,r) | GroundE(x,r) | StartsWith @"\(" (_,TyE(x,StartsWith @"\)" (_,r))) -> Some(x,r) 
| _ -> None 
and (|ParamE|_|) = function 
| StartsWith "'[a-zA-Z0-9]+" (s,r) -> Some(Param s, r) 
| _ -> None 
and (|GroundE|_|) = function 
| StartsWith "[`.a-zA-Z0-9]+" (gnd, StartsWith "<" (_, Delimit "," (|TyE|_|) (l, StartsWith ">" (_,r)))) -> 
     let ty = FSharpAssembly.FSharpLibrary.GetEntity gnd |> getType 
     Some(Complex(Ground(ty), l), r) 
| StartsWith "[`.a-zA-Z0-9]+" (gnd, r) -> 
     let ty = FSharpAssembly.FSharpLibrary.GetEntity gnd |> getType 
     Some(Complex(Ground(ty), []), r) 
| _ -> None 

(* parse a string into a type *) 
let parse (s:string) = 
    (* remove whitespace before matching *) 
    match s.Replace(" ","") with 
    | TyE(ty,"") -> ty 
    | _ -> failwith "Not a well-formed type" 

(* an infinite stream of possible variable names - for performing renaming *) 
let rec names = 
    let letters = ['a' .. 'z'] |> List.map string 
    seq { 
    yield! letters 
    for n in names do 
     for l in letters do 
     yield n + l 
    } 

(* finds entities in the F# library with the requested signature, modulo type parameter unification *) 
let find s = 
    let ty = parse s 
    let vars = usedVars ty 
    seq { 
    for e in FSharpAssembly.FSharpLibrary.Entities do 
    for m in e.MembersOrValues do 
     (* need try/catch to avoid error on weird types like "[]`1" *) 
     match (try Some(cvt m.Type) with _ -> None) with 
     | Some ty2 -> 
     (* rename all type variables from the query to avoid incorrectly unifying with type variables in signatures *) 
     let used = usedVars ty2 
     let newVars = Seq.choose (fun v -> if Set.contains v used then None else Some(Param v)) names 
     let varMap = Map.ofSeq (Seq.zip vars newVars) 
     let ty = Map.fold (fun t v p -> subst v p t) ty varMap 
     match mgu ty ty2 with 
     | None ->() 
     | Some _ -> yield sprintf "%s.%s.%s" e.Namespace e.DisplayName m.DisplayName 
     | _ ->() } 
+0

, gracias, esto se ve muy bien. Cuando tenga tiempo, intentaré convertir esto en algo tan útil como Hoogle. –

5

Ésta es la última y más grande: http://fsdn.azurewebsites.net/

A partir de los documentos: https://github.com/fsdn-projects/FSDN

soportados firmas API

API signature      Query example 
Functions and values in modules int -> string 
Fields of records and structs  Ref<'a> => 'a 
Methods and properties   'a list -> int or 'a list => int 
Constructors      string -> Uri 
Names (function and method names) head : 'a list -> 'a 
Active patterns     (||) : ... -> Expr -> ? 
Cuestiones relacionadas