2010-02-12 26 views
19

Nói rằng tôi muốn biết nếu F # có chức năng thư viện các loạiTài liệu F # có cách tìm kiếm các chức năng theo loại của chúng không?

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

tức, một cái gì đó mà đếm có bao nhiêu mục của một danh sách đó một hàm trả về true cho. (hoặc trả về chỉ mục của mục đầu tiên trả về đúng)

Tôi đã sử dụng danh sách lớn tại trang MSR cho F # trước khi tài liệu trên MSDN sẵn sàng. Tôi chỉ có thể tìm kiếm trang cho văn bản trên vì các loại được liệt kê. Nhưng bây giờ tài liệu MSDN chỉ liệt kê các loại trên các trang riêng lẻ - trang mô-đun là một mẩu văn bản mô tả. Google kinda-sorta hoạt động nhưng không thể trợ giúp với

// 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 có chương trình độc lập cho tên gọi này là Hoogle. F # có tương đương như Fing hay gì đó không?

Trả lời

10

Dựa trên câu trả lời của kvb, tôi đã tạo một ứng dụng hoàn chỉnh. Nó được lưu trữ trên github tại http://github.com/sandersn/fing.

Mã vẫn còn khá xấu, nhưng nó hoạt động cho các trường hợp đơn giản. Tôi đã lấy ra phần mở rộng chung nhất của kvb (mgu) bởi vì nó cho biết thêm rất nhiều kết quả không rõ ràng. Những thứ lạ mắt như ràng buộc cấu trúc và kiểu siêu chung nhất cũng không hoạt động.

Cũng có nhị phân cho phiên bản dòng lệnh nếu bạn không muốn xây dựng từ nguồn. (Nó vẫn yêu cầu một phiên bản hiện đại của thời gian chạy .NET được cài đặt, mặc dù.) Cuối cùng tôi sẽ tìm thấy một số ASP.NET lưu trữ, tìm hiểu ASP, và bọc toàn bộ điều trong một ứng dụng web để cài đặt không cần thiết ở tất cả. (Tôi đoán nếu có nhu cầu tôi có thể tạo một GUI phía máy khách, nhưng tôi thậm chí còn ít kinh nghiệm hơn với loại điều đó.)

17

Tôi không biết bất kỳ công cụ nào như vậy. Tuy nhiên nó có thể là một bài tập thú vị để viết một bằng cách sử dụng System.Reflection (hoặc thậm chí tốt hơn, thư viện siêu dữ liệu trong PowerPack), để bạn có thể lấy các tên biến kiểu tương tự như modulo, v.v.

EDIT - Tôi đã đúng - nó đã một bài tập thú vị. Những gì sau đây có rất nhiều mụn cóc, nhưng không quá tệ cho ~ 150 dòng mã. Hy vọng rằng điều này sẽ là đủ để có được một ai đó bắt đầu những người muốn làm việc trên một công cụ thực sự. Nó không làm bất cứ điều gì tiên tiến như kiểm tra các chức năng với các tham số sắp xếp lại, và thư viện siêu dữ liệu là một chút cầu kỳ về việc sử dụng tên đầy đủ, do đó bạn cần phải cẩn thận một chút. Để trả lời các câu hỏi trong bài ban đầu của bạn, tôi thực hiện

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

và nhận được danh sách sau đây của các ứng cử viên:

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 

Trong số này, chỉ List.findIndex có chính xác loại generic bạn đang tìm kiếm, nhưng với sự kết hợp đúng các thông số loại để thực hiện các thông số khác (ví dụ: nếu 'a = int thì List.find có loại mong muốn). Thật không may, các ràng buộc không được tính đến trong tìm kiếm để các hàm không List không thể thực sự khớp.

Nếu không có thêm quảng cáo, đây là mã tôi đã sử dụng - bạn sẽ cần phải thêm tham chiếu đến cụm FSharp.PowerPack.Metadata để làm cho nó hoạt động.

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

Cảm ơn, điều này có vẻ tuyệt vời. Khi tôi có thời gian, tôi sẽ cố gắng làm việc này thành một thứ có thể sử dụng như Hoogle. –

5

Đây là mới nhất và lớn nhất: http://fsdn.azurewebsites.net/

Từ các tài liệu: https://github.com/fsdn-projects/FSDN

chữ ký API hỗ trợ

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 -> ? 
Các vấn đề liên quan